diff --git a/.changes/190be92b-a106-476b-99b3-224a80f81d7d.json b/.changes/190be92b-a106-476b-99b3-224a80f81d7d.json new file mode 100644 index 00000000000..ef54d2e55c0 --- /dev/null +++ b/.changes/190be92b-a106-476b-99b3-224a80f81d7d.json @@ -0,0 +1,6 @@ +{ + "id": "190be92b-a106-476b-99b3-224a80f81d7d", + "type": "bugfix", + "description": "**Breaking**: Make some types for various services optional by removing default values", + "requiresMinorVersionBump": true +} \ No newline at end of file diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/RemoveDefaults.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/RemoveDefaults.kt new file mode 100644 index 00000000000..836f3fc6ae7 --- /dev/null +++ b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/RemoveDefaults.kt @@ -0,0 +1,108 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package aws.sdk.kotlin.codegen.customization + +import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.kotlin.codegen.KotlinSettings +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.model.hasTrait +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.utils.ToSmithyBuilder + +/** + * Removes the default value of certain shapes, and any member that target + * those shapes,for certain services. These default values may cause + * serialization, validation, or other unexpected issues. + */ +class RemoveDefaults : KotlinIntegration { + // Service shape id -> Shape id of each root shape to remove default values from. + private val removeDefaultsFrom = mapOf( + ShapeId.from("com.amazonaws.amplifyuibuilder#AmplifyUIBuilder") to setOf( + ShapeId.from("com.amazonaws.amplifyuibuilder#ListComponentsLimit"), + ShapeId.from("com.amazonaws.amplifyuibuilder#ListFormsLimit"), + ShapeId.from("com.amazonaws.amplifyuibuilder#ListThemesLimit"), + ), + ShapeId.from("com.amazonaws.drs#ElasticDisasterRecoveryService") to setOf( + ShapeId.from("com.amazonaws.drs#Validity"), + ShapeId.from("com.amazonaws.drs#CostOptimizationConfiguration\$burstBalanceThreshold"), + ShapeId.from("com.amazonaws.drs#CostOptimizationConfiguration\$burstBalanceDeltaThreshold"), + ShapeId.from("com.amazonaws.drs#ListStagingAccountsRequest\$maxResults"), + ShapeId.from("com.amazonaws.drs#StrictlyPositiveInteger"), + ShapeId.from("com.amazonaws.drs#MaxResultsType"), + ShapeId.from("com.amazonaws.drs#MaxResultsReplicatingSourceServers"), + ShapeId.from("com.amazonaws.drs#LaunchActionOrder"), + ), + ShapeId.from("com.amazonaws.evidently#Evidently") to setOf( + ShapeId.from("com.amazonaws.evidently#ResultsPeriod"), + ), + ShapeId.from("com.amazonaws.location#LocationService") to setOf( + ShapeId.from("com.amazonaws.location#ListPlaceIndexesRequest\$MaxResults"), + ShapeId.from("com.amazonaws.location#SearchPlaceIndexForSuggestionsRequest\$MaxResults"), + ShapeId.from("com.amazonaws.location#PlaceIndexSearchResultLimit"), + ), + ShapeId.from("com.amazonaws.paymentcryptographydata#PaymentCryptographyDataPlane") to setOf( + ShapeId.from("com.amazonaws.paymentcryptographydata#IntegerRangeBetween4And12"), + ), + ShapeId.from("com.amazonaws.emrserverless#AwsToledoWebService") to setOf( + ShapeId.from("com.amazonaws.emrserverless#WorkerCounts"), + ), + ) + + override val order: Byte = 0 + + override fun enabledForService(model: Model, settings: KotlinSettings): Boolean = + settings.service in removeDefaultsFrom + + override fun preprocessModel(model: Model, settings: KotlinSettings): Model { + val serviceId = settings.service + val removeDefaultsFromShapes = removeDefaultsFrom[serviceId] + ?: throw CodegenException("expected $serviceId in removed defaults map") + return removeDefaults(model, removeDefaultsFromShapes) + } + + fun removeDefaults(model: Model, fromShapes: Set): Model { + val removedRootDefaults: MutableSet = HashSet() + val removedRootDefaultsModel = ModelTransformer.create().mapShapes(model) { + if (shouldRemoveRootDefault(it, fromShapes)) { + removedRootDefaults.add(it.id) + removeDefault(it) + } else { + it + } + } + + return ModelTransformer.create().mapShapes(removedRootDefaultsModel) { + if (shouldRemoveMemberDefault(it, removedRootDefaults, fromShapes)) { + removeDefault(it) + } else { + it + } + } + } + + private fun shouldRemoveRootDefault(shape: Shape, removeDefaultsFrom: Set): Boolean = + shape !is MemberShape && shape.id in removeDefaultsFrom && shape.hasTrait() + + private fun shouldRemoveMemberDefault( + shape: Shape, + removedRootDefaults: Set, + removeDefaultsFrom: Set, + ): Boolean = shape is MemberShape && + // Check the original set of shapes to remove for this shape id, to remove members that were in that set. + (shape.target in removedRootDefaults || shape.id in removeDefaultsFrom) && + shape.hasTrait() + + private fun removeDefault(shape: Shape): Shape = + ((shape as ToSmithyBuilder<*>).toBuilder() as AbstractShapeBuilder<*, *>) + .removeTrait(DefaultTrait.ID) + .build() +} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration b/codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration index d21433dc632..ba938f303c8 100644 --- a/codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +++ b/codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration @@ -32,3 +32,4 @@ aws.sdk.kotlin.codegen.customization.machinelearning.MachineLearningEndpointCust aws.sdk.kotlin.codegen.customization.route53.TrimResourcePrefix aws.sdk.kotlin.codegen.customization.route53.ChangeResourceRecordSetsUnmarshallingIntegration aws.sdk.kotlin.codegen.customization.ec2.EC2MakePrimitivesOptional +aws.sdk.kotlin.codegen.customization.RemoveDefaults diff --git a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/aws/sdk/kotlin/codegen/customization/RemoveDefaultsTest.kt b/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/aws/sdk/kotlin/codegen/customization/RemoveDefaultsTest.kt new file mode 100644 index 00000000000..03a7f5037e4 --- /dev/null +++ b/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/aws/sdk/kotlin/codegen/customization/RemoveDefaultsTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package aws.sdk.kotlin.codegen.customization + +import org.junit.jupiter.api.Test +import software.amazon.smithy.kotlin.codegen.model.hasTrait +import software.amazon.smithy.kotlin.codegen.test.toSmithyModel +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.DefaultTrait +import kotlin.test.assertFalse + +class RemoveDefaultsTest { + @Test + fun removesDefaults() { + val model = """ + ${"$"}version: "2.0" + + namespace test + + structure Foo { + bar: Bar = 0 + baz: Integer = 0 + } + + @default(0) + integer Bar + + """.toSmithyModel() + + val removeDefaultsFrom = setOf(ShapeId.from("test#Bar"), ShapeId.from("test#Foo\$baz")) + val transformed = RemoveDefaults().removeDefaults(model, removeDefaultsFrom) + val barMember = transformed.expectShape(ShapeId.from("test#Foo\$bar")) + assertFalse(barMember.hasTrait()) + val bazMember = transformed.expectShape(ShapeId.from("test#Foo\$baz")) + assertFalse(bazMember.hasTrait()) + val root = transformed.expectShape(ShapeId.from("test#Bar")) + assertFalse(root.hasTrait()) + } +}