From 18ab19b4bd4de5a7785bfb94b44f3d740e2b5e3f Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 15 Mar 2022 10:41:25 -0500 Subject: [PATCH 1/3] add: default list of http versions add: type alias for list of http versions add: decorator to add desired http versions to property bag fix: gradle warning for argument "model" update: CHANGELOG --- CHANGELOG.next.toml | 6 ++++ .../HttpVersionListGenerator.kt | 36 +++++++++++++++++++ .../customize/RequiredCustomizations.kt | 10 +++--- .../smithy/customize/RustCodegenDecorator.kt | 4 +-- rust-runtime/aws-smithy-http/Cargo.toml | 1 + .../aws-smithy-http/src/http_versions.rs | 17 +++++++++ rust-runtime/aws-smithy-http/src/lib.rs | 1 + 7 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt create mode 100644 rust-runtime/aws-smithy-http/src/http_versions.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index daaf0c0e05..60dc68128b 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -133,3 +133,9 @@ functional in a future update. references = ["smithy-rs#724"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "Velfi" + +[[smithy-rs]] +message = "HTTP request property bag now contains list of desired HTTP versions to use when making requests. This list is not currently used but will be in an upcoming update." +references = ["smithy-rs#1257"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "Velfi" diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt new file mode 100644 index 0000000000..0c99329a9a --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt @@ -0,0 +1,36 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.smithy.customizations + +import software.amazon.smithy.rust.codegen.rustlang.Writable +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection + +private fun RuntimeConfig.httpVersionModule(): RuntimeType = RuntimeType("http_versions", this.runtimeCrate("http"), "aws_smithy_http") +private fun RuntimeConfig.defaultHttpVersionList(): RuntimeType = this.httpVersionModule().member("DEFAULT_HTTP_VERSION_LIST") + +class HttpVersionListGenerator( + private val codegenContext: CodegenContext, +) : OperationCustomization() { + override fun section(section: OperationSection): Writable { + return when (section) { + is OperationSection.MutateRequest -> writable { + val runtimeConfig = codegenContext.runtimeConfig + rust( + """ + ${section.request}.properties_mut().insert(${runtimeConfig.defaultHttpVersionList().fullyQualifiedName()}.clone()); + """ + ) + } + else -> emptySection + } + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt index 246645d8aa..eb874d217d 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.smithy.customizations.AllowLintsGener import software.amazon.smithy.rust.codegen.smithy.customizations.CrateVersionGenerator import software.amazon.smithy.rust.codegen.smithy.customizations.EndpointPrefixGenerator import software.amazon.smithy.rust.codegen.smithy.customizations.HttpChecksumRequiredGenerator +import software.amazon.smithy.rust.codegen.smithy.customizations.HttpVersionListGenerator import software.amazon.smithy.rust.codegen.smithy.customizations.IdempotencyTokenGenerator import software.amazon.smithy.rust.codegen.smithy.customizations.SmithyTypesPubUseGenerator import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization @@ -30,10 +31,11 @@ class RequiredCustomizations : RustCodegenDecorator { operation: OperationShape, baseCustomizations: List ): List { - return baseCustomizations + IdempotencyTokenGenerator(codegenContext, operation) + EndpointPrefixGenerator( - codegenContext, - operation - ) + HttpChecksumRequiredGenerator(codegenContext, operation) + return baseCustomizations + + IdempotencyTokenGenerator(codegenContext, operation) + + EndpointPrefixGenerator(codegenContext, operation) + + HttpChecksumRequiredGenerator(codegenContext, operation) + + HttpVersionListGenerator(codegenContext) } override fun libRsCustomizations( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt index 8b5db7a7ab..cc3d0725ad 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt @@ -139,8 +139,8 @@ open class CombinedCodegenDecorator(decorators: List) : Ru return orderedDecorators.forEach { it.extras(codegenContext, rustCrate) } } - override fun transformModel(service: ServiceShape, baseModel: Model): Model { - return orderedDecorators.foldRight(baseModel) { decorator, model -> + override fun transformModel(service: ServiceShape, model: Model): Model { + return orderedDecorators.foldRight(model) { decorator, model -> decorator.transformModel(service, model) } } diff --git a/rust-runtime/aws-smithy-http/Cargo.toml b/rust-runtime/aws-smithy-http/Cargo.toml index 90dad8887d..a4db84d7dc 100644 --- a/rust-runtime/aws-smithy-http/Cargo.toml +++ b/rust-runtime/aws-smithy-http/Cargo.toml @@ -18,6 +18,7 @@ bytes = "1" bytes-utils = "0.1" http = "0.2.3" http-body = "0.4.4" +once_cell = "1.10" percent-encoding = "2.1.0" pin-project = "1" tracing = "0.1" diff --git a/rust-runtime/aws-smithy-http/src/http_versions.rs b/rust-runtime/aws-smithy-http/src/http_versions.rs new file mode 100644 index 0000000000..5305f1d495 --- /dev/null +++ b/rust-runtime/aws-smithy-http/src/http_versions.rs @@ -0,0 +1,17 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +//! HTTP Version-related code + +use http::Version as HttpVersion; +use once_cell::sync::Lazy; + +/// A list of supported or desired HttpVersions. Typically use when requesting an HTTP Client from a +/// client cache. +pub type HttpVersionList = Vec; + +/// The default list of desired HTTP protocol versions to use when making requests +pub const DEFAULT_HTTP_VERSION_LIST: Lazy = + Lazy::new(|| vec![HttpVersion::HTTP_11]); diff --git a/rust-runtime/aws-smithy-http/src/lib.rs b/rust-runtime/aws-smithy-http/src/lib.rs index ece1c477c3..71ac6b1ec2 100644 --- a/rust-runtime/aws-smithy-http/src/lib.rs +++ b/rust-runtime/aws-smithy-http/src/lib.rs @@ -20,6 +20,7 @@ pub mod body; pub mod endpoint; pub mod header; +pub mod http_versions; pub mod label; pub mod middleware; pub mod operation; From a5b6d1281967f0b73919efd29b909fc4ff5f7758 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 15 Mar 2022 11:02:50 -0500 Subject: [PATCH 2/3] fix: clippy lint --- rust-runtime/aws-smithy-http/src/http_versions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-runtime/aws-smithy-http/src/http_versions.rs b/rust-runtime/aws-smithy-http/src/http_versions.rs index 5305f1d495..5ea3420f1e 100644 --- a/rust-runtime/aws-smithy-http/src/http_versions.rs +++ b/rust-runtime/aws-smithy-http/src/http_versions.rs @@ -13,5 +13,5 @@ use once_cell::sync::Lazy; pub type HttpVersionList = Vec; /// The default list of desired HTTP protocol versions to use when making requests -pub const DEFAULT_HTTP_VERSION_LIST: Lazy = +pub static DEFAULT_HTTP_VERSION_LIST: Lazy = Lazy::new(|| vec![HttpVersion::HTTP_11]); From 7ee7e4f7f6954a2e408d166b6515f3e86dd8a1b0 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 16 Mar 2022 15:38:32 -0500 Subject: [PATCH 3/3] update: use more accurate name for test RustCodegenDecorators rename: HttpVersionListGenerator.kt to HttpVersionListCustomization.kt update: HttpVersionListCustomization to grab versions from models when possible fix: various docs issues reported by IDE add: new "Extras" section to ServiceConfig update: code broke by "Extras" change add: tests for http version sniffing --- .../rust/codegen/smithy/RustCodegenPlugin.kt | 2 +- .../HttpVersionListCustomization.kt | 69 ++++ .../HttpVersionListGenerator.kt | 36 --- .../customizations/RetryConfigDecorator.kt | 1 + .../customizations/SleepImplDecorator.kt | 1 + .../customizations/TimeoutConfigDecorator.kt | 1 + .../customize/RequiredCustomizations.kt | 4 +- .../smithy/customize/RustCodegenDecorator.kt | 6 +- .../config/ServiceConfigGenerator.kt | 8 + .../serialize/JsonSerializerGenerator.kt | 2 +- .../smithy/rust/codegen/testutil/Rust.kt | 19 +- .../testutil/TestConfigCustomization.kt | 1 + .../HttpVersionListGeneratorTest.kt | 303 ++++++++++++++++++ .../generators/EndpointTraitBindingsTest.kt | 4 +- .../config/ServiceConfigGeneratorTest.kt | 1 + 15 files changed, 410 insertions(+), 48 deletions(-) create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListCustomization.kt delete mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt create mode 100644 codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt index 89b7b6189b..fe91768c19 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt @@ -43,7 +43,7 @@ class RustCodegenPlugin : SmithyBuildPlugin { /** SymbolProvider * 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 funcitonality, then is layered + * 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(model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig) = diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListCustomization.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListCustomization.kt new file mode 100644 index 0000000000..d16516590c --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListCustomization.kt @@ -0,0 +1,69 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.smithy.customizations + +import software.amazon.smithy.aws.traits.protocols.AwsProtocolTrait +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.rustlang.Writable +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.util.getTrait +import software.amazon.smithy.rust.codegen.util.isEventStream + +private fun RuntimeConfig.httpVersionModule(): RuntimeType = + RuntimeType("http_versions", this.runtimeCrate("http"), "aws_smithy_http") +private fun RuntimeConfig.defaultHttpVersionList(): RuntimeType = + this.httpVersionModule().member("DEFAULT_HTTP_VERSION_LIST") + +class HttpVersionListCustomization( + private val codegenContext: CodegenContext, + private val operationShape: OperationShape +) : OperationCustomization() { + private val defaultHttpVersions = codegenContext.runtimeConfig.defaultHttpVersionList().fullyQualifiedName() + + override fun section(section: OperationSection): Writable { + val awsProtocolTrait = codegenContext.serviceShape.getTrait() + val supportedHttpProtocolVersions = if (awsProtocolTrait == null) { + // No protocol trait was defined, use default http versions + "$defaultHttpVersions.clone()" + } else { + // Figure out whether we're dealing with an EventStream operation and fetch the corresponding list of desired HTTP versions + val versionList = if (operationShape.isEventStream(codegenContext.model)) awsProtocolTrait.eventStreamHttp else awsProtocolTrait.http + if (versionList.isEmpty()) { + // If no desired versions are specified, go with the default + "$defaultHttpVersions.clone()" + } else { + // otherwise, use the specified versions + "vec![${versionList.joinToString(",") { version -> mapHttpVersion(version) }}]" + } + } + + return when (section) { + is OperationSection.MutateRequest -> writable { + rust( + """ + ${section.request}.properties_mut().insert($supportedHttpProtocolVersions); + """ + ) + } + else -> emptySection + } + } +} + +// Map an ALPN protocol ID to a version from the `http` Rust crate +private fun mapHttpVersion(httpVersion: String): String { + return when (httpVersion) { + "http/1.1" -> "http::Version::HTTP_11" + "h2" -> "http::Version::HTTP_2" + else -> TODO("Unsupported HTTP version '$httpVersion', please check your model") + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt deleted file mode 100644 index 0c99329a9a..0000000000 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/HttpVersionListGenerator.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.rust.codegen.smithy.customizations - -import software.amazon.smithy.rust.codegen.rustlang.Writable -import software.amazon.smithy.rust.codegen.rustlang.rust -import software.amazon.smithy.rust.codegen.rustlang.writable -import software.amazon.smithy.rust.codegen.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection - -private fun RuntimeConfig.httpVersionModule(): RuntimeType = RuntimeType("http_versions", this.runtimeCrate("http"), "aws_smithy_http") -private fun RuntimeConfig.defaultHttpVersionList(): RuntimeType = this.httpVersionModule().member("DEFAULT_HTTP_VERSION_LIST") - -class HttpVersionListGenerator( - private val codegenContext: CodegenContext, -) : OperationCustomization() { - override fun section(section: OperationSection): Writable { - return when (section) { - is OperationSection.MutateRequest -> writable { - val runtimeConfig = codegenContext.runtimeConfig - rust( - """ - ${section.request}.properties_mut().insert(${runtimeConfig.defaultHttpVersionList().fullyQualifiedName()}.clone()); - """ - ) - } - else -> emptySection - } - } -} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt index 308b950775..bacb4b40b8 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt @@ -143,6 +143,7 @@ class RetryConfigProviderConfig(codegenContext: CodegenContext) : ConfigCustomiz """retry_config: self.retry_config,""", *codegenScope ) + else -> emptySection } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt index ba2a05f30a..25bcf2e5ae 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt @@ -211,6 +211,7 @@ class SleepImplProviderConfig(codegenContext: CodegenContext) : ConfigCustomizat """sleep_impl: self.sleep_impl,""", *codegenScope ) + else -> emptySection } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt index ab9407c0f6..a01fb058e1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt @@ -185,6 +185,7 @@ class TimeoutConfigProviderConfig(codegenContext: CodegenContext) : ConfigCustom """timeout_config: self.timeout_config,""", *codegenScope ) + else -> emptySection } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt index eb874d217d..5f9bbd6268 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt @@ -13,7 +13,7 @@ import software.amazon.smithy.rust.codegen.smithy.customizations.AllowLintsGener import software.amazon.smithy.rust.codegen.smithy.customizations.CrateVersionGenerator import software.amazon.smithy.rust.codegen.smithy.customizations.EndpointPrefixGenerator import software.amazon.smithy.rust.codegen.smithy.customizations.HttpChecksumRequiredGenerator -import software.amazon.smithy.rust.codegen.smithy.customizations.HttpVersionListGenerator +import software.amazon.smithy.rust.codegen.smithy.customizations.HttpVersionListCustomization import software.amazon.smithy.rust.codegen.smithy.customizations.IdempotencyTokenGenerator import software.amazon.smithy.rust.codegen.smithy.customizations.SmithyTypesPubUseGenerator import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization @@ -35,7 +35,7 @@ class RequiredCustomizations : RustCodegenDecorator { IdempotencyTokenGenerator(codegenContext, operation) + EndpointPrefixGenerator(codegenContext, operation) + HttpChecksumRequiredGenerator(codegenContext, operation) + - HttpVersionListGenerator(codegenContext) + HttpVersionListCustomization(codegenContext, operation) } override fun libRsCustomizations( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt index cc3d0725ad..b7102ce691 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt @@ -26,7 +26,7 @@ import java.util.logging.Logger * [RustCodegenDecorator] allows downstream users to customize code generation. * * For example, AWS-specific code generation generates customizations required to support - * AWS services. A different downstream customer way wish to add a different set of derive + * AWS services. A different downstream customer may wish to add a different set of derive * attributes to the generated classes. */ interface RustCodegenDecorator { @@ -140,8 +140,8 @@ open class CombinedCodegenDecorator(decorators: List) : Ru } override fun transformModel(service: ServiceShape, model: Model): Model { - return orderedDecorators.foldRight(model) { decorator, model -> - decorator.transformModel(service, model) + return orderedDecorators.foldRight(model) { decorator, otherModel -> + decorator.transformModel(service, otherModel) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGenerator.kt index 1cf8c685e8..c71011384e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGenerator.kt @@ -76,6 +76,11 @@ sealed class ServiceConfig(name: String) : Section(name) { * ``` */ object BuilderBuild : ServiceConfig("BuilderBuild") + + /** + * A section for extra functionality that needs to be defined with the config module + */ + object Extras : ServiceConfig("Extras") } fun ServiceShape.needsIdempotencyToken(model: Model): Boolean { @@ -172,5 +177,8 @@ class ServiceConfigGenerator(private val customizations: List { +fun generatePluginContext(model: Model, additionalSettings: ObjectNode = ObjectNode.builder().build(), addModuleToEventStreamAllowList: Boolean = false): Pair { val testDir = TestWorkspace.subproject() val moduleName = "test_${testDir.nameWithoutExtension}" val testPath = testDir.toPath() val manifest = FileManifest.create(testPath) - val settings = Node.objectNodeBuilder() + var settingsBuilder = Node.objectNodeBuilder() .withMember("module", Node.from(moduleName)) .withMember("moduleVersion", Node.from("1.0.0")) .withMember("moduleDescription", Node.from("test")) @@ -138,6 +139,18 @@ fun generatePluginContext(model: Model): Pair { Node.from((TestRuntimeConfig.runtimeCrateLocation).path) ).build() ) + + if (addModuleToEventStreamAllowList) { + settingsBuilder = settingsBuilder.withMember( + "codegen", + Node.objectNodeBuilder().withMember( + "eventStreamAllowList", + Node.fromStrings(moduleName) + ).build() + ) + } + + val settings = settingsBuilder.merge(additionalSettings) .build() val pluginContext = PluginContext.builder().model(model).fileManifest(manifest).settings(settings).build() return pluginContext to testPath @@ -163,7 +176,7 @@ class TestWriterDelegator(private val fileManifest: FileManifest, symbolProvider /** * Setting `runClippy` to true can be helpful when debugging clippy failures, but - * should generally be set to false to avoid invalidating the Cargo cache between + * should generally be set to `false` to avoid invalidating the Cargo cache between * every unit test run. */ fun TestWriterDelegator.compileAndTest(runClippy: Boolean = false) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt index c2794613f2..3fcab9ae57 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt @@ -33,6 +33,7 @@ fun stubCustomization(name: String): ConfigCustomization { $name: self.$name.unwrap_or(123), """ ) + else -> emptySection } } } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt new file mode 100644 index 0000000000..7473ea7fe5 --- /dev/null +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt @@ -0,0 +1,303 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.customizations + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.rustlang.Writable +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.CodegenVisitor +import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustCrate +import software.amazon.smithy.rust.codegen.smithy.customize.CombinedCodegenDecorator +import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator +import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.testutil.TokioTest +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.testutil.generatePluginContext +import software.amazon.smithy.rust.codegen.util.runCommand + +// If any of these tests fail, and you want to understand why, run them with logging: +// ``` +// ./gradlew codegen:test --tests software.amazon.smithy.rust.codegen.customizations.HttpVersionListGeneratorTest --info +// ``` + +internal class HttpVersionListGeneratorTest { + @Test + fun `http version list integration test (no preferred version)`() { + val model = """ + namespace com.example + + use aws.protocols#awsJson1_0 + + @awsJson1_0 + @aws.api#service(sdkId: "Test", endpointPrefix: "differentPrefix") + service TestService { + operations: [SayHello], + version: "1" + } + + @endpoint(hostPrefix: "test123.{greeting}.") + operation SayHello { + input: SayHelloInput + } + + structure SayHelloInput { + @required + @hostLabel + greeting: String + } + """.asSmithyModel() + val (ctx, testDir) = generatePluginContext(model) + val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') + val testWriter = object : RustCodegenDecorator { + override val name: String = "add tests" + override val order: Byte = 0 + override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + rustCrate.withFile("tests/validate_defaults.rs") { + TokioTest.render(it) + it.rust( + """ + async fn test_http_version_list_defaults() { + let conf = $moduleName::Config::builder().build(); + let op = $moduleName::operation::SayHello::builder() + .greeting("hello") + .build().expect("valid operation") + .make_operation(&conf).await.expect("hello is a valid prefix"); + let properties = op.properties(); + let actual_http_versions = properties.get::>() + .expect("http versions list should be in property bag"); + let expected_http_versions = &vec![http::Version::HTTP_11]; + assert_eq!(actual_http_versions, expected_http_versions); + } + """ + ) + } + } + } + val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(testWriter)) + visitor.execute() + "cargo test".runCommand(testDir) + } + + @Test + fun `http version list integration test (http)`() { + val model = """ + namespace com.example + + use aws.protocols#restJson1 + + @restJson1(http: ["http/1.1", "h2"]) + @aws.api#service(sdkId: "Test", endpointPrefix: "differentPrefix") + service TestService { + operations: [SayHello], + version: "1" + } + + @http(method: "PUT", uri: "/input", code: 200) + @idempotent + @endpoint(hostPrefix: "test123.{greeting}.") + operation SayHello { + input: SayHelloInput + } + + structure SayHelloInput { + @required + @hostLabel + greeting: String + } + """.asSmithyModel() + val (ctx, testDir) = generatePluginContext(model) + val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') + val testWriter = object : RustCodegenDecorator { + override val name: String = "add tests" + override val order: Byte = 0 + override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + rustCrate.withFile("tests/validate_http.rs") { + TokioTest.render(it) + it.rust( + """ + async fn test_http_version_list_defaults() { + let conf = $moduleName::Config::builder().build(); + let op = $moduleName::operation::SayHello::builder() + .greeting("hello") + .build().expect("valid operation") + .make_operation(&conf).await.expect("hello is a valid prefix"); + let properties = op.properties(); + let actual_http_versions = properties.get::>() + .expect("http versions list should be in property bag"); + let expected_http_versions = &vec![http::Version::HTTP_11, http::Version::HTTP_2]; + assert_eq!(actual_http_versions, expected_http_versions); + } + """ + ) + } + } + } + val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(testWriter)) + visitor.execute() + "cargo test".runCommand(testDir) + } + + @Test + fun `http version list integration test (eventStreamHttp)`() { + val model = """ + namespace com.example + + use aws.protocols#restJson1 + + @restJson1(http: ["h2", "http/1.1"], eventStreamHttp: ["h2"]) + @aws.api#service(sdkId: "Test") + service TestService { + operations: [SayHello], + version: "1" + } + + @idempotent + @http(uri: "/test", method: "PUT") + operation SayHello { + input: SayHelloInput, + output: SayHelloOutput + } + + structure SayHelloInput {} + + structure SayHelloOutput { + @httpPayload + greeting: SomeStream + } + + structure Something { stuff: SomethingElse } + + structure SomethingElse { + version: String + } + + @streaming + union SomeStream { + Something: Something, + } + """.asSmithyModel() + + val (ctx, testDir) = generatePluginContext(model, addModuleToEventStreamAllowList = true) + val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') + val codegenDecorator = object : RustCodegenDecorator { + override val name: String = "add tests" + override val order: Byte = 0 + + override fun configCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List + ): List { + return super.configCustomizations(codegenContext, baseCustomizations) + FakeSigningConfig(codegenContext.runtimeConfig) + } + + override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + rustCrate.withFile("tests/validate_eventstream_http.rs") { + TokioTest.render(it) + it.rust( + """ + async fn test_http_version_list_defaults() { + let conf = $moduleName::Config::builder().build(); + let op = $moduleName::operation::SayHello::builder() + .build().expect("valid operation") + .make_operation(&conf).await.unwrap(); + let properties = op.properties(); + let actual_http_versions = properties.get::>() + .expect("http versions list should be in property bag"); + let expected_http_versions = &vec![http::Version::HTTP_2]; + assert_eq!(actual_http_versions, expected_http_versions); + } + """ + ) + } + } + } + + val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(codegenDecorator)) + visitor.execute() + "cargo test".runCommand(testDir) + } +} + +class FakeSigningConfig( + runtimeConfig: RuntimeConfig, +) : ConfigCustomization() { + private val codegenScope = arrayOf( + "SharedPropertyBag" to RuntimeType( + "SharedPropertyBag", + CargoDependency.SmithyHttp(runtimeConfig), + "aws_smithy_http::property_bag" + ), + "SignMessageError" to RuntimeType( + "SignMessageError", + CargoDependency.SmithyEventStream(runtimeConfig), + "aws_smithy_eventstream::frame" + ), + "SignMessage" to RuntimeType( + "SignMessage", + CargoDependency.SmithyEventStream(runtimeConfig), + "aws_smithy_eventstream::frame" + ), + "Message" to RuntimeType( + "Message", + CargoDependency.SmithyEventStream(runtimeConfig), + "aws_smithy_eventstream::frame" + ) + ) + + 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) -> Result<#{Message}, #{SignMessageError}> { + Ok(#{Message}::new(Vec::new())) + } + } + """, + *codegenScope + ) + } + else -> emptySection + } + } +} diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt index 0b43212e92..8780f02b2a 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt @@ -141,7 +141,7 @@ internal class EndpointTraitBindingsTest { """.asSmithyModel() val (ctx, testDir) = generatePluginContext(model) val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') - val testWriter = object : RustCodegenDecorator { + val codegenDecorator = object : RustCodegenDecorator { override val name: String = "add tests" override val order: Byte = 0 override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { @@ -169,7 +169,7 @@ internal class EndpointTraitBindingsTest { } } } - val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(testWriter)) + val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(codegenDecorator)) visitor.execute() "cargo test".runCommand(testDir) } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt index 0794aa7557..8b61138133 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -87,6 +87,7 @@ internal class ServiceConfigGeneratorTest { ServiceConfig.BuilderStruct -> writable { rust("config_field: Option") } ServiceConfig.BuilderImpl -> emptySection ServiceConfig.BuilderBuild -> writable { rust("config_field: self.config_field.unwrap_or_default(),") } + else -> emptySection } } }