Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove invalid defaults for some services #3217

Merged
merged 4 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ message = "The `AssumeRoleBuilder::policy_arns` now accepts strings instead of a
references = ["smithy-rs#3205"]
meta = { "breaking" = true, "tada" = false, "bug" = false }
author = "rcoh"

[[aws-sdk-rust]]
message = "Make certain types for EMR Serverless optional. Previously, they defaulted to 0, but this created invalid requests."
references = ["smithy-rs#3217"]
meta = { "breaking" = true, "tada" = false, "bug" = true }
author = "milesziemer"
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.DocsRsMe
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator
import software.amazon.smithy.rustsdk.customize.DisabledAuthDecorator
import software.amazon.smithy.rustsdk.customize.RemoveDefaultsDecorator
import software.amazon.smithy.rustsdk.customize.apigateway.ApiGatewayDecorator
import software.amazon.smithy.rustsdk.customize.applyDecorators
import software.amazon.smithy.rustsdk.customize.ec2.Ec2Decorator
Expand Down Expand Up @@ -53,6 +54,7 @@ val DECORATORS: List<ClientCodegenDecorator> = listOf(
RecursionDetectionDecorator(),
InvocationIdDecorator(),
RetryInformationHeaderDecorator(),
RemoveDefaultsDecorator(),
),

// Service specific decorators
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rustsdk.customize

import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.AbstractShapeBuilder
import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.traits.DefaultTrait
import software.amazon.smithy.model.transform.ModelTransformer
import software.amazon.smithy.rust.codegen.core.util.hasTrait
import software.amazon.smithy.rust.codegen.core.util.letIf
import software.amazon.smithy.utils.ToSmithyBuilder
import java.util.logging.Logger

/**
* Removes default values from specified root shapes, and any members that target those
* root shapes.
*/
object RemoveDefaults {
private val logger: Logger = Logger.getLogger(javaClass.name)

fun processModel(model: Model, removeDefaultsFrom: Set<ShapeId>): Model {
val removedRootDefaults: MutableSet<ShapeId> = HashSet()
val removedRootDefaultsModel = ModelTransformer.create().mapShapes(model) { shape ->
shape.letIf(shouldRemoveRootDefault(shape, removeDefaultsFrom)) {
logger.info("Removing default trait from root $shape")
removedRootDefaults.add(shape.id)
removeDefault(shape)
}
}

return ModelTransformer.create().mapShapes(removedRootDefaultsModel) { shape ->
shape.letIf(shouldRemoveMemberDefault(shape, removedRootDefaults)) {
logger.info("Removing default trait from member $shape")
removeDefault(shape)
}
}
}

private fun shouldRemoveRootDefault(shape: Shape, removeDefaultsFrom: Set<ShapeId>): Boolean {
return shape !is MemberShape && removeDefaultsFrom.contains(shape.id) && shape.hasTrait<DefaultTrait>()
}

private fun shouldRemoveMemberDefault(shape: Shape, removeDefaultsFrom: Set<ShapeId>): Boolean {
return shape is MemberShape && removeDefaultsFrom.contains(shape.target) && shape.hasTrait<DefaultTrait>()
}

private fun removeDefault(shape: Shape): Shape {
return ((shape as ToSmithyBuilder<*>).toBuilder() as AbstractShapeBuilder<*, *>)
.removeTrait(DefaultTrait.ID)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -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.customize

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
import software.amazon.smithy.rust.codegen.core.util.shapeId
import java.util.logging.Logger

/**
* Removes default values from certain shapes, and any member that targets those shapes,
* for some services where the default value causes serialization issues, validation
* issues, or other unexpected behavior.
*/
class RemoveDefaultsDecorator : ClientCodegenDecorator {
override val name: String = "RemoveDefaults"
override val order: Byte = 0
private val logger: Logger = Logger.getLogger(javaClass.name)

// Service shape id -> Shape id of each root shape to remove the default from.
private val removeDefaults = mapOf(
"com.amazonaws.emrserverless#AwsToledoWebService".shapeId() to setOf(
// Service expects this to have a min value > 0
"com.amazonaws.emrserverless#WorkerCounts".shapeId(),
),
)

private fun applies(service: ServiceShape) =
removeDefaults.containsKey(service.id)

override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model {
if (!applies(service)) {
return model
}
logger.info("Removing invalid defaults from ${service.id}")
return RemoveDefaults.processModel(model, removeDefaults[service.id]!!)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rustsdk.customize

import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import software.amazon.smithy.model.shapes.IntegerShape
import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.traits.DefaultTrait
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.util.hasTrait
import software.amazon.smithy.rust.codegen.core.util.lookup
import software.amazon.smithy.rust.codegen.core.util.shapeId

internal class RemoveDefaultsTest {
@Test
fun `defaults should be removed`() {
val removeDefaults = setOf(
"test#Bar".shapeId(),
)
val baseModel = """
namespace test

structure Foo {
bar: Bar = 0
}

@default(0)
integer Bar

""".asSmithyModel(smithyVersion = "2.0")
val model = RemoveDefaults.processModel(baseModel, removeDefaults)
val member = model.lookup<MemberShape>("test#Foo\$bar")
member.hasTrait<DefaultTrait>() shouldBe false
val root = model.lookup<IntegerShape>("test#Bar")
root.hasTrait<DefaultTrait>() shouldBe false
}
}