Skip to content

Commit

Permalink
Merge branch 'main' into refactor/add-smithy-orchestrator
Browse files Browse the repository at this point in the history
  • Loading branch information
Velfi authored Mar 13, 2023
2 parents 0ad5c0a + a339f6b commit e695355
Show file tree
Hide file tree
Showing 19 changed files with 271 additions and 61 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,15 @@ message = "Add more client re-exports. Specifically, it re-exports `aws_smithy_h
references = ["smithy-rs#2437", "aws-sdk-rust#600"]
meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" }
author = "ysaito1001"

[[aws-sdk-rust]]
message = "Enable presigning for S3's `HeadObject` operation."
references = ["aws-sdk-rust#753", "smithy-rs#2451"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "Velfi"

[[smithy-rs]]
message = "Smithy members named `send` were previously renamed to `send_value` at codegen time. These will now be called `send` in the generated code."
references = ["smithy-rs#2382"]
meta = { "breaking" = true, "tada" = false, "bug" = true, "target" = "server" }
author = "jdisanti"
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ internal val PRESIGNABLE_OPERATIONS by lazy {
mapOf(
// S3
// TODO(https://github.com/awslabs/aws-sdk-rust/issues/488) Technically, all S3 operations support presigning
ShapeId.from("com.amazonaws.s3#HeadObject") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD),
ShapeId.from("com.amazonaws.s3#GetObject") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD),
ShapeId.from("com.amazonaws.s3#PutObject") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD),
ShapeId.from("com.amazonaws.s3#UploadPart") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD),
Expand Down
1 change: 1 addition & 0 deletions aws/sdk/integration-tests/no-default-features/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ publish = false
[dev-dependencies]
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" }
futures = "0.3.25"
tokio = { version = "1.8.4", features = ["full", "test-util"] }
tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ async fn test_clients_from_sdk_config() {
}

// This will fail due to lack of a connector when constructing the service client
#[test]
#[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."
)]
fn test_clients_from_service_config() {
let config = aws_sdk_s3::Config::builder().build();
async fn test_clients_from_service_config() {
#[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 {
todo!()
}
}

let config = aws_sdk_s3::Config::builder()
.sleep_impl(std::sync::Arc::new(StubSleep {}))
.build();
// This will panic due to the lack of an HTTP connector
aws_sdk_s3::Client::from_conf(config);
}
15 changes: 15 additions & 0 deletions aws/sdk/integration-tests/s3/tests/presigning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,18 @@ async fn test_presigning_object_lambda() -> Result<(), Box<dyn Error>> {
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(())
}

#[tokio::test]
async fn test_presigned_head_object() -> Result<(), Box<dyn Error>> {
let presigned = presign_input!(s3::input::HeadObjectInput::builder()
.bucket("bucket")
.key("key")
.build()?);

assert_eq!("HEAD", presigned.method().as_str());
assert_eq!(
presigned.uri().to_string(),
"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",
);
Ok(())
}
Original file line number Diff line number Diff line change
@@ -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 software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordConfig
import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator
import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator

val ClientReservedWords = RustReservedWordConfig(
structureMemberMap = StructureGenerator.structureMemberNameMap +
mapOf(
"send" to "send_value",
// To avoid conflicts with the `make_operation` and `presigned` functions on generated inputs
"make_operation" to "make_operation_value",
"presigned" to "presigned_value",
"customize" to "customize_value",
// To avoid conflicts with the error metadata `meta` field
"meta" to "meta_value",
),
unionMemberMap = mapOf(
// Unions contain an `Unknown` variant. This exists to support parsing data returned from the server
// that represent union variants that have been added since this SDK was generated.
UnionGenerator.UnknownVariantName to "${UnionGenerator.UnknownVariantName}Value",
"${UnionGenerator.UnknownVariantName}Value" to "${UnionGenerator.UnknownVariantName}Value_",
),
enumMemberMap = mapOf(
// Unknown is used as the name of the variant containing unexpected values
"Unknown" to "UnknownValue",
// Real models won't end in `_` so it's safe to stop here
"UnknownValue" to "UnknownValue_",
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() {
.let { StreamingShapeMetadataProvider(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) }
.let { RustReservedWordSymbolProvider(it, ClientReservedWords) }
// Allows decorators to inject a custom symbol provider
.let { codegenDecorator.symbolProvider(it) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,23 @@ import software.amazon.smithy.model.shapes.UnionShape
import software.amazon.smithy.model.traits.EnumTrait
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.generators.UnionGenerator
import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom
import software.amazon.smithy.rust.codegen.core.util.hasTrait
import software.amazon.smithy.rust.codegen.core.util.letIf

class RustReservedWordSymbolProvider(private val base: RustSymbolProvider) : WrappingSymbolProvider(base) {
data class RustReservedWordConfig(
/** Map of struct member names that should get renamed */
val structureMemberMap: Map<String, String>,
/** Map of union member names that should get renamed */
val unionMemberMap: Map<String, String>,
/** Map of enum member names that should get renamed */
val enumMemberMap: Map<String, String>,
)

class RustReservedWordSymbolProvider(
private val base: RustSymbolProvider,
private val reservedWordConfig: RustReservedWordConfig,
) : WrappingSymbolProvider(base) {
private val internal =
ReservedWordSymbolProvider.builder().symbolProvider(base)
.nameReservedWords(RustReservedWords)
Expand All @@ -33,35 +44,14 @@ class RustReservedWordSymbolProvider(private val base: RustSymbolProvider) : Wra
val reservedWordReplacedName = internal.toMemberName(shape)
val container = model.expectShape(shape.container)
return when {
container is StructureShape -> when (baseName) {
"build" -> "build_value"
"builder" -> "builder_value"
"default" -> "default_value"
"send" -> "send_value"
// To avoid conflicts with the `make_operation` and `presigned` functions on generated inputs
"make_operation" -> "make_operation_value"
"presigned" -> "presigned_value"
"customize" -> "customize_value"
// To avoid conflicts with the error metadata `meta` field
"meta" -> "meta_value"
else -> reservedWordReplacedName
}
container is StructureShape ->
reservedWordConfig.structureMemberMap.getOrDefault(baseName, reservedWordReplacedName)

container is UnionShape -> when (baseName) {
// Unions contain an `Unknown` variant. This exists to support parsing data returned from the server
// that represent union variants that have been added since this SDK was generated.
UnionGenerator.UnknownVariantName -> "${UnionGenerator.UnknownVariantName}Value"
"${UnionGenerator.UnknownVariantName}Value" -> "${UnionGenerator.UnknownVariantName}Value_"
else -> reservedWordReplacedName
}
container is UnionShape ->
reservedWordConfig.unionMemberMap.getOrDefault(baseName, reservedWordReplacedName)

container is EnumShape || container.hasTrait<EnumTrait>() -> when (baseName) {
// Unknown is used as the name of the variant containing unexpected values
"Unknown" -> "UnknownValue"
// Real models won't end in `_` so it's safe to stop here
"UnknownValue" -> "UnknownValue_"
else -> reservedWordReplacedName
}
container is EnumShape || container.hasTrait<EnumTrait>() ->
reservedWordConfig.enumMemberMap.getOrDefault(baseName, reservedWordReplacedName)

else -> error("unexpected container: $container")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ open class StructureGenerator(
private val shape: StructureShape,
private val customizations: List<StructureCustomization>,
) {
companion object {
/** Reserved struct member names */
val structureMemberNameMap: Map<String, String> = mapOf(
"build" to "build_value",
"builder" to "builder_value",
"default" to "default_value",
)
}

private val errorTrait = shape.getTrait<ErrorTrait>()
protected val members: List<MemberShape> = shape.allMembers.values.toList()
private val accessorMembers: List<MemberShape> = when (errorTrait) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import software.amazon.smithy.model.shapes.UnionShape
import software.amazon.smithy.model.traits.ErrorTrait
import software.amazon.smithy.rust.codegen.core.rustlang.Attribute
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordConfig
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords
import software.amazon.smithy.rust.codegen.core.rustlang.Visibility
Expand Down Expand Up @@ -138,13 +139,21 @@ fun String.asSmithyModel(sourceLocation: String? = null, smithyVersion: String =
}

// Intentionally only visible to codegen-core since the other modules have their own symbol providers
internal fun testSymbolProvider(model: Model): RustSymbolProvider = SymbolVisitor(
internal fun testSymbolProvider(
model: Model,
rustReservedWordConfig: RustReservedWordConfig? = null,
): RustSymbolProvider = SymbolVisitor(
testRustSettings(),
model,
ServiceShape.builder().version("test").id("test#Service").build(),
TestRustSymbolProviderConfig,
).let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf(Attribute.NonExhaustive)) }
.let { RustReservedWordSymbolProvider(it) }
.let {
RustReservedWordSymbolProvider(
it,
rustReservedWordConfig ?: RustReservedWordConfig(emptyMap(), emptyMap(), emptyMap()),
)
}

// Intentionally only visible to codegen-core since the other modules have their own contexts
internal fun testCodegenContext(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,108 @@ import software.amazon.smithy.rust.codegen.core.util.lookup
internal class RustReservedWordSymbolProviderTest {
private class TestSymbolProvider(model: Model) :
WrappingSymbolProvider(SymbolVisitor(testRustSettings(), model, null, TestRustSymbolProviderConfig))
private val emptyConfig = RustReservedWordConfig(emptyMap(), emptyMap(), emptyMap())

@Test
fun `structs are escaped`() {
val model = """
namespace test
structure Self {}
""".asSmithyModel()
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model))
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model), emptyConfig)
val symbol = provider.toSymbol(model.lookup("test#Self"))
symbol.name shouldBe "SelfValue"
}

private fun mappingTest(config: RustReservedWordConfig, model: Model, id: String, test: (String) -> Unit) {
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model), config)
val symbol = provider.toMemberName(model.lookup("test#Container\$$id"))
test(symbol)
}

@Test
fun `structs member names are mapped via config`() {
val config = emptyConfig.copy(
structureMemberMap = mapOf(
"name_to_map" to "mapped_name",
"NameToMap" to "MappedName",
),
)
var model = """
namespace test
structure Container {
name_to_map: String
}
""".asSmithyModel()
mappingTest(config, model, "name_to_map") { memberName ->
memberName shouldBe "mapped_name"
}

model = """
namespace test
enum Container {
NameToMap = "NameToMap"
}
""".asSmithyModel(smithyVersion = "2.0")
mappingTest(config, model, "NameToMap") { memberName ->
// Container was not a struct, so the field keeps its old name
memberName shouldBe "NameToMap"
}

model = """
namespace test
union Container {
NameToMap: String
}
""".asSmithyModel()
mappingTest(config, model, "NameToMap") { memberName ->
// Container was not a struct, so the field keeps its old name
memberName shouldBe "NameToMap"
}
}

@Test
fun `union member names are mapped via config`() {
val config = emptyConfig.copy(
unionMemberMap = mapOf(
"name_to_map" to "mapped_name",
"NameToMap" to "MappedName",
),
)

var model = """
namespace test
union Container {
NameToMap: String
}
""".asSmithyModel()
mappingTest(config, model, "NameToMap") { memberName ->
memberName shouldBe "MappedName"
}

model = """
namespace test
structure Container {
name_to_map: String
}
""".asSmithyModel()
mappingTest(config, model, "name_to_map") { memberName ->
// Container was not a union, so the field keeps its old name
memberName shouldBe "name_to_map"
}

model = """
namespace test
enum Container {
NameToMap = "NameToMap"
}
""".asSmithyModel(smithyVersion = "2.0")
mappingTest(config, model, "NameToMap") { memberName ->
// Container was not a union, so the field keeps its old name
memberName shouldBe "NameToMap"
}
}

@Test
fun `member names are escaped`() {
val model = """
Expand All @@ -42,7 +132,7 @@ internal class RustReservedWordSymbolProviderTest {
async: String
}
""".asSmithyModel()
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model))
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model), emptyConfig)
provider.toMemberName(
MemberShape.builder().id("namespace#container\$async").target("namespace#Integer").build(),
) shouldBe "r##async"
Expand All @@ -58,7 +148,15 @@ internal class RustReservedWordSymbolProviderTest {
namespace foo
@enum([{ name: "dontcare", value: "dontcare" }]) string Container
""".asSmithyModel()
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model))
val provider = RustReservedWordSymbolProvider(
TestSymbolProvider(model),
reservedWordConfig = emptyConfig.copy(
enumMemberMap = mapOf(
"Unknown" to "UnknownValue",
"UnknownValue" to "UnknownValue_",
),
),
)

fun expectEnumRename(original: String, expected: MaybeRenamed) {
val symbol = provider.toSymbol(
Expand Down
Loading

0 comments on commit e695355

Please sign in to comment.