From 2f0a9e0da7ce7a86f21f0f95380c2d009235d278 Mon Sep 17 00:00:00 2001 From: Ian Botsford <83236726+ianbotsf@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:50:23 +0000 Subject: [PATCH 1/5] fix: correctly codegen maps with enum keys --- .../a7f82a33-11f1-4184-97af-ff713e922dfc.json | 9 +++ .../smithy/kotlin/codegen/KotlinSettings.kt | 3 + .../kotlin/codegen/core/KotlinDelegator.kt | 1 + .../codegen/core/KotlinSymbolProvider.kt | 18 +++-- .../codegen/rendering/ShapeValueGenerator.kt | 11 ++- .../serde/DeserializeStructGenerator.kt | 69 ++++++++++++++--- .../serde/SerializeStructGenerator.kt | 77 ++++++++++++++----- .../rendering/serde/XmlParserGenerator.kt | 12 +-- .../kotlin/codegen/core/SymbolProviderTest.kt | 43 ++++++++++- .../rendering/ShapeValueGeneratorTest.kt | 46 +++++++++++ .../serde/DeserializeStructGeneratorTest.kt | 51 ++++++++++++ .../serde/SerializeStructGeneratorTest.kt | 40 ++++++++++ .../smithy/kotlin/tests/serde/XmlMapTest.kt | 6 +- .../test/resources/kitchen-sink-model.smithy | 9 +++ 14 files changed, 340 insertions(+), 55 deletions(-) create mode 100644 .changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json diff --git a/.changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json b/.changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json new file mode 100644 index 000000000..6e4e756a5 --- /dev/null +++ b/.changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json @@ -0,0 +1,9 @@ +{ + "id": "a7f82a33-11f1-4184-97af-ff713e922dfc", + "type": "bugfix", + "description": "⚠️ **IMPORTANT**: Fix codegen for map shapes which use string enums as map keys. See the Map Enum Keys breaking change announcement (link coming soon) for more details", + "issues": [ + "awslabs/smithy-kotlin#1045" + ], + "requiresMinorVersionBump": true +} diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/KotlinSettings.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/KotlinSettings.kt index 5e7911972..25942d3cc 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/KotlinSettings.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/KotlinSettings.kt @@ -43,6 +43,7 @@ data class KotlinSettings( val sdkId: String, val build: BuildSettings = BuildSettings.Default, val api: ApiSettings = ApiSettings.Default, + val debug: Boolean = false, ) { /** @@ -104,12 +105,14 @@ data class KotlinSettings( val sdkId = config.getStringMemberOrDefault(SDK_ID, serviceId.name) val build = config.getObjectMember(BUILD_SETTINGS) val api = config.getObjectMember(API_SETTINGS) + val debug = config.getBooleanMemberOrDefault("debug", false) return KotlinSettings( serviceId, PackageSettings(packageName, version, desc), sdkId, BuildSettings.fromNode(build), ApiSettings.fromNode(api), + debug, ) } } diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinDelegator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinDelegator.kt index f185a66d6..7b5b871e6 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinDelegator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinDelegator.kt @@ -150,6 +150,7 @@ class KotlinDelegator( val needsNewline = writers.containsKey(formattedFilename) val writer = writers.getOrPut(formattedFilename) { val kotlinWriter = KotlinWriter(namespace) + if (settings.debug) kotlinWriter.enableStackTraceComments(true) // Register all integrations [SectionWriterBindings] on the writer. integrations.forEach { integration -> diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinSymbolProvider.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinSymbolProvider.kt index 10dd958ce..b2848653d 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinSymbolProvider.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/KotlinSymbolProvider.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.kotlin.codegen.core import software.amazon.smithy.codegen.core.* import software.amazon.smithy.kotlin.codegen.KotlinSettings -import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes import software.amazon.smithy.kotlin.codegen.lang.kotlinReservedWords import software.amazon.smithy.kotlin.codegen.model.* import software.amazon.smithy.kotlin.codegen.utils.dq @@ -162,15 +161,18 @@ class KotlinSymbolProvider(private val model: Model, private val settings: Kotli } override fun mapShape(shape: MapShape): Symbol { - val reference = toSymbol(shape.value) - val valueSuffix = if (reference.isNullable) "?" else "" - val valueType = "${reference.name}$valueSuffix" - val fullyQualifiedValueType = "${reference.fullName}$valueSuffix" + val keyReference = toSymbol(shape.key) + val keyType = keyReference.name + val fullyQualifiedKeyType = keyReference.fullName + + val valueReference = toSymbol(shape.value) + val valueSuffix = if (valueReference.isNullable) "?" else "" + val valueType = "${valueReference.name}$valueSuffix" + val fullyQualifiedValueType = "${valueReference.fullName}$valueSuffix" - val keyType = KotlinTypes.String.name - val fullyQualifiedKeyType = KotlinTypes.String.fullName return createSymbolBuilder(shape, "Map<$keyType, $valueType>") - .addReferences(reference) + .addReferences(keyReference) + .addReferences(valueReference) .putProperty(SymbolProperty.FULLY_QUALIFIED_NAME_HINT, "Map<$fullyQualifiedKeyType, $fullyQualifiedValueType>") .putProperty(SymbolProperty.MUTABLE_COLLECTION_FUNCTION, "mutableMapOf<$keyType, $valueType>") .putProperty(SymbolProperty.IMMUTABLE_COLLECTION_FUNCTION, "mapOf<$keyType, $valueType>") diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGenerator.kt index 885febc11..3ac683da8 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGenerator.kt @@ -188,7 +188,16 @@ class ShapeValueGenerator( } is MapShape -> { memberShape = generator.model.expectShape(currShape.value.target) - writer.writeInline("#S to ", keyNode.value) + + val keyTarget = generator.model.expectShape(currShape.key.target) + if (keyTarget.isEnum) { + val keySymbol = generator.symbolProvider.toSymbol(currShape.key) + writer.writeInline("#T.fromValue(#S)", keySymbol, keyNode.value) + } else { + writer.writeInline("#S", keyNode.value) + } + + writer.writeInline(" to ") if (valueNode is NullNode) { writer.write("null") diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGenerator.kt index 2f8e8de51..ea91afc9f 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.kotlin.codegen.rendering.serde import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.kotlin.codegen.core.* import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes import software.amazon.smithy.kotlin.codegen.model.* @@ -145,9 +146,10 @@ open class DeserializeStructGenerator( .indent() .withBlock("deserializer.#T($descriptorName) {", "}", RuntimeTypes.Serde.deserializeMap) { write( - "val #L = #T()", + "val #L = #T<#T, #T#L>()", mutableCollectionName, KotlinTypes.Collections.mutableMapOf, + ctx.symbolProvider.toSymbol(targetShape.key), ctx.symbolProvider.toSymbol(targetShape.value), nullabilitySuffix(targetShape.isSparse), ) @@ -168,6 +170,8 @@ open class DeserializeStructGenerator( nestingLevel: Int, parentMemberName: String, ) { + val keyShape = ctx.model.expectShape(mapShape.key.target) + val keySymbol = ctx.symbolProvider.toSymbol(keyShape) val elementShape = ctx.model.expectShape(mapShape.value.target) val isSparse = mapShape.isSparse @@ -187,21 +191,47 @@ open class DeserializeStructGenerator( ShapeType.TIMESTAMP, ShapeType.ENUM, ShapeType.INT_ENUM, - -> renderEntry(elementShape, nestingLevel, isSparse, parentMemberName) + -> renderEntry(keyShape, keySymbol, elementShape, nestingLevel, isSparse, parentMemberName) ShapeType.SET, ShapeType.LIST, - -> renderListEntry(rootMemberShape, elementShape as CollectionShape, nestingLevel, isSparse, parentMemberName) + -> renderListEntry( + rootMemberShape, + keyShape, + keySymbol, + elementShape as CollectionShape, + nestingLevel, + isSparse, + parentMemberName, + ) + + ShapeType.MAP -> renderMapEntry( + rootMemberShape, + keyShape, + keySymbol, + elementShape as MapShape, + nestingLevel, + isSparse, + parentMemberName, + ) - ShapeType.MAP -> renderMapEntry(rootMemberShape, elementShape as MapShape, nestingLevel, isSparse, parentMemberName) ShapeType.UNION, ShapeType.STRUCTURE, - -> renderNestedStructureEntry(elementShape, nestingLevel, isSparse, parentMemberName) + -> renderNestedStructureEntry(keyShape, keySymbol, elementShape, nestingLevel, isSparse, parentMemberName) else -> error("Unhandled type ${elementShape.type}") } } + private fun writeKeyVal(keyShape: Shape, keySymbol: Symbol, keyName: String) { + writer.writeInline("val $keyName = ") + if (keyShape.isEnum) { + writer.write("#T.fromValue(key())", keySymbol) + } else { + writer.write("key()") + } + } + /** * Renders the deserialization of a nested structure contained in a map. Example: * @@ -212,6 +242,8 @@ open class DeserializeStructGenerator( * ``` */ private fun renderNestedStructureEntry( + keyShape: Shape, + keySymbol: Symbol, elementShape: Shape, nestingLevel: Int, isSparse: Boolean, @@ -226,7 +258,7 @@ open class DeserializeStructGenerator( writer.addImport(symbol) } - writer.write("val $keyName = key()") + writeKeyVal(keyShape, keySymbol, keyName) writer.write("val $valueName = if (nextHasValue()) { $deserializerFn } else { deserializeNull()$populateNullValuePostfix }") writer.write("$parentMemberName[$keyName] = $valueName") } @@ -247,6 +279,8 @@ open class DeserializeStructGenerator( */ private fun renderMapEntry( rootMemberShape: MemberShape, + keyShape: Shape, + keySymbol: Symbol, mapShape: MapShape, nestingLevel: Int, isSparse: Boolean, @@ -260,14 +294,15 @@ open class DeserializeStructGenerator( val memberName = nextNestingLevel.variableNameFor(NestedIdentifierType.MAP) val collectionReturnExpression = collectionReturnExpression(rootMemberShape, memberName) - writer.write("val $keyName = key()") + writeKeyVal(keyShape, keySymbol, keyName) writer.withBlock("val $valueName =", "") { withBlock("if (nextHasValue()) {", "} else { deserializeNull()$populateNullValuePostfix }") { withBlock("deserializer.#T($descriptorName) {", "}", RuntimeTypes.Serde.deserializeMap) { write( - "val #L = #T()", + "val #L = #T<#T, #T#L>()", memberName, KotlinTypes.Collections.mutableMapOf, + keySymbol, ctx.symbolProvider.toSymbol(mapShape.value), nullabilitySuffix(mapShape.isSparse), ) @@ -298,6 +333,8 @@ open class DeserializeStructGenerator( */ private fun renderListEntry( rootMemberShape: MemberShape, + keyShape: Shape, + keySymbol: Symbol, collectionShape: CollectionShape, nestingLevel: Int, isSparse: Boolean, @@ -311,7 +348,7 @@ open class DeserializeStructGenerator( val memberName = nextNestingLevel.variableNameFor(NestedIdentifierType.COLLECTION) val collectionReturnExpression = collectionReturnExpression(rootMemberShape, memberName) - writer.write("val $keyName = key()") + writeKeyVal(keyShape, keySymbol, keyName) writer.withBlock("val $valueName =", "") { withBlock("if (nextHasValue()) {", "} else { deserializeNull()$populateNullValuePostfix }") { withBlock("deserializer.#T($descriptorName) {", "}", RuntimeTypes.Serde.deserializeList) { @@ -340,13 +377,20 @@ open class DeserializeStructGenerator( * map0[k0] = el0 * ``` */ - private fun renderEntry(elementShape: Shape, nestingLevel: Int, isSparse: Boolean, parentMemberName: String) { + private fun renderEntry( + keyShape: Shape, + keySymbol: Symbol, + elementShape: Shape, + nestingLevel: Int, + isSparse: Boolean, + parentMemberName: String, + ) { val deserializerFn = deserializerForShape(elementShape) val keyName = nestingLevel.variableNameFor(NestedIdentifierType.KEY) val valueName = nestingLevel.variableNameFor(NestedIdentifierType.VALUE) val populateNullValuePostfix = if (isSparse) "" else "; continue" - writer.write("val $keyName = key()") + writeKeyVal(keyShape, keySymbol, keyName) writer.write("val $valueName = if (nextHasValue()) { $deserializerFn } else { deserializeNull()$populateNullValuePostfix }") writer.write("$parentMemberName[$keyName] = $valueName") } @@ -476,9 +520,10 @@ open class DeserializeStructGenerator( writer.withBlock("val $elementName = deserializer.#T($descriptorName) {", "}", RuntimeTypes.Serde.deserializeMap) { write( - "val #L = #T()", + "val #L = #T<#T, #T#L>()", mapName, KotlinTypes.Collections.mutableMapOf, + ctx.symbolProvider.toSymbol(mapShape.key), ctx.symbolProvider.toSymbol(mapShape.value), nullabilitySuffix(mapShape.isSparse), ) diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGenerator.kt index ff099b0c9..20be3a6b6 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGenerator.kt @@ -175,6 +175,7 @@ open class SerializeStructGenerator( * Delegates to other functions based on the type of value target of map. */ protected fun delegateMapSerialization(rootMemberShape: MemberShape, mapShape: MapShape, nestingLevel: Int, parentMemberName: String) { + val keyShape = ctx.model.expectShape(mapShape.key.target) val elementShape = ctx.model.expectShape(mapShape.value.target) val isSparse = mapShape.isSparse @@ -192,18 +193,41 @@ open class SerializeStructGenerator( ShapeType.BIG_INTEGER, ShapeType.ENUM, ShapeType.INT_ENUM, - -> renderPrimitiveEntry(elementShape, nestingLevel, parentMemberName) + -> renderPrimitiveEntry(keyShape, elementShape, nestingLevel, parentMemberName) + + ShapeType.BLOB -> renderBlobEntry(keyShape, nestingLevel, parentMemberName) + + ShapeType.TIMESTAMP -> renderTimestampEntry( + keyShape, + mapShape.value, + elementShape, + nestingLevel, + parentMemberName, + ) - ShapeType.BLOB -> renderBlobEntry(nestingLevel, parentMemberName) - ShapeType.TIMESTAMP -> renderTimestampEntry(mapShape.value, elementShape, nestingLevel, parentMemberName) ShapeType.SET, ShapeType.LIST, - -> renderListEntry(rootMemberShape, elementShape as CollectionShape, nestingLevel, isSparse, parentMemberName) + -> renderListEntry( + rootMemberShape, + keyShape, + elementShape as CollectionShape, + nestingLevel, + isSparse, + parentMemberName, + ) + + ShapeType.MAP -> renderMapEntry( + rootMemberShape, + keyShape, + elementShape as MapShape, + nestingLevel, + isSparse, + parentMemberName, + ) - ShapeType.MAP -> renderMapEntry(rootMemberShape, elementShape as MapShape, nestingLevel, isSparse, parentMemberName) ShapeType.UNION, ShapeType.STRUCTURE, - -> renderNestedStructureEntry(elementShape, nestingLevel, parentMemberName, isSparse) + -> renderNestedStructureEntry(keyShape, elementShape, nestingLevel, parentMemberName, isSparse) else -> error("Unhandled type ${elementShape.type}") } @@ -276,6 +300,7 @@ open class SerializeStructGenerator( * ``` */ private fun renderNestedStructureEntry( + keyShape: Shape, structureShape: Shape, nestingLevel: Int, parentMemberName: String, @@ -283,13 +308,14 @@ open class SerializeStructGenerator( ) { val serializerTypeName = ctx.symbolProvider.toSymbol(structureShape).documentSerializerName() val (keyName, valueName) = keyValueNames(nestingLevel) + val keyValue = keyValue(keyShape, keyName) val containerName = if (nestingLevel == 0) "input." else "" val value = "asSdkSerializable($valueName, ::$serializerTypeName)" when (isSparse) { - true -> writer.write("$containerName$parentMemberName.forEach { ($keyName, $valueName) -> if ($valueName != null) entry($keyName, $value) else entry($keyName, null as String?) }") - false -> writer.write("$containerName$parentMemberName.forEach { ($keyName, $valueName) -> entry($keyName, $value) }") + true -> writer.write("$containerName$parentMemberName.forEach { ($keyName, $valueName) -> if ($valueName != null) entry($keyValue, $value) else entry($keyValue, null as String?) }") + false -> writer.write("$containerName$parentMemberName.forEach { ($keyName, $valueName) -> entry($keyValue, $value) }") } } @@ -337,6 +363,7 @@ open class SerializeStructGenerator( */ private fun renderMapEntry( rootMemberShape: MemberShape, + keyShape: Shape, mapShape: MapShape, nestingLevel: Int, isSparse: Boolean, @@ -345,11 +372,12 @@ open class SerializeStructGenerator( val descriptorName = rootMemberShape.descriptorName(nestingLevel.nestedDescriptorName()) val containerName = if (nestingLevel == 0) "input." else "" val (keyName, valueName) = keyValueNames(nestingLevel) + val keyValue = keyValue(keyShape, keyName) val parentName = parentName(valueName) writer.withBlock("$containerName$parentMemberName.forEach { ($keyName, $valueName) ->", "}") { - writer.wrapBlockIf(isSparse, "if ($valueName != null) {", "} else entry($keyName, null as String?)") { - writer.withBlock("mapEntry($keyName, $descriptorName) {", "}") { + writer.wrapBlockIf(isSparse, "if ($valueName != null) {", "} else entry($keyValue, null as String?)") { + writer.withBlock("mapEntry($keyValue, $descriptorName) {", "}") { delegateMapSerialization(rootMemberShape, mapShape, nestingLevel + 1, parentName) } } @@ -367,6 +395,7 @@ open class SerializeStructGenerator( */ private fun renderListEntry( rootMemberShape: MemberShape, + keyShape: Shape, elementShape: CollectionShape, nestingLevel: Int, isSparse: Boolean, @@ -376,10 +405,11 @@ open class SerializeStructGenerator( val containerName = if (nestingLevel == 0) "input." else "" val (keyName, valueName) = keyValueNames(nestingLevel) val parentName = parentName(valueName) + val keyValue = keyValue(keyShape, keyName) writer.withBlock("$containerName$parentMemberName.forEach { ($keyName, $valueName) ->", "}") { - writer.wrapBlockIf(isSparse, "if ($valueName != null) {", "} else entry($keyName, null as String?)") { - writer.withBlock("listEntry($keyName, $descriptorName) {", "}") { + writer.wrapBlockIf(isSparse, "if ($valueName != null) {", "} else entry($keyValue, null as String?)") { + writer.withBlock("listEntry($keyValue, $descriptorName) {", "}") { delegateListSerialization(rootMemberShape, elementShape, nestingLevel + 1, parentName) } } @@ -417,12 +447,13 @@ open class SerializeStructGenerator( * c0.forEach { (key1, value1) -> entry(key1, value1) } * ``` */ - private fun renderPrimitiveEntry(elementShape: Shape, nestingLevel: Int, listMemberName: String) { + private fun renderPrimitiveEntry(keyShape: Shape, elementShape: Shape, nestingLevel: Int, listMemberName: String) { val containerName = if (nestingLevel == 0) "input." else "" val enumPostfix = if (elementShape.isEnum) ".value" else "" val (keyName, valueName) = keyValueNames(nestingLevel) + val keyValue = keyValue(keyShape, keyName) - writer.write("$containerName$listMemberName.forEach { ($keyName, $valueName) -> entry($keyName, $valueName$enumPostfix) }") + writer.write("$containerName$listMemberName.forEach { ($keyName, $valueName) -> entry($keyValue, $valueName$enumPostfix) }") } /** @@ -432,12 +463,13 @@ open class SerializeStructGenerator( * input.fooBlobMap.forEach { (key, value) -> entry(key, value.encodeBase64String()) } * ``` */ - private fun renderBlobEntry(nestingLevel: Int, listMemberName: String) { + private fun renderBlobEntry(keyShape: Shape, nestingLevel: Int, listMemberName: String) { val containerName = if (nestingLevel == 0) "input." else "" val (keyName, valueName) = keyValueNames(nestingLevel) + val keyValue = keyValue(keyShape, keyName) writer.write( - "$containerName$listMemberName.forEach { ($keyName, $valueName) -> entry($keyName, $valueName.#T()) }", + "$containerName$listMemberName.forEach { ($keyName, $valueName) -> entry($keyValue, $valueName.#T()) }", RuntimeTypes.Core.Text.Encoding.encodeBase64String, ) } @@ -449,7 +481,13 @@ open class SerializeStructGenerator( * input.fooTimestampMap.forEach { (key, value) -> entry(key, it, TimestampFormat.EPOCH_SECONDS) } * ``` */ - private fun renderTimestampEntry(memberShape: Shape, elementShape: Shape, nestingLevel: Int, listMemberName: String) { + private fun renderTimestampEntry( + keyShape: Shape, + memberShape: Shape, + elementShape: Shape, + nestingLevel: Int, + listMemberName: String, + ) { writer.addImport(RuntimeTypes.Core.TimestampFormat) // favor the member shape if it overrides the value shape trait @@ -466,9 +504,10 @@ open class SerializeStructGenerator( .toRuntimeEnum() val (keyName, valueName) = keyValueNames(nestingLevel) + val keyValue = keyValue(keyShape, keyName) val containerName = if (nestingLevel == 0) "input." else "" - writer.write("$containerName$listMemberName.forEach { ($keyName, $valueName) -> entry($keyName, it, $tsFormat) }") + writer.write("$containerName$listMemberName.forEach { ($keyName, $valueName) -> entry($keyValue, it, $tsFormat) }") } /** @@ -659,6 +698,8 @@ open class SerializeStructGenerator( return keyName to valueName } + private fun keyValue(keyShape: Shape, keyName: String) = keyName + if (keyShape.isEnum) ".value" else "" + /** * Get the name of the `PrimitiveSerializer` function name for the corresponding shape type * @throws CodegenException when no known function name for the given type is known to exist diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/XmlParserGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/XmlParserGenerator.kt index 4c4e59894..35051bec5 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/XmlParserGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/XmlParserGenerator.kt @@ -463,7 +463,7 @@ open class XmlParserGenerator( writer: KotlinWriter, ) { val target = ctx.model.expectShape(member.target) - val keySymbol = KotlinTypes.String + val keySymbol = ctx.symbolProvider.toSymbol(target.key) val valueSymbol = ctx.symbolProvider.toSymbol(target.value) writer.addImportReferences(valueSymbol, SymbolReference.ContextOption.USE) val isSparse = target.hasTrait() @@ -512,7 +512,7 @@ open class XmlParserGenerator( map: MapShape, ): Symbol { val shapeName = StringUtils.capitalize(map.id.getName(ctx.service)) - val keySymbol = KotlinTypes.String + val keySymbol = ctx.symbolProvider.toSymbol(map.key) val valueSymbol = ctx.symbolProvider.toSymbol(map.value) val isSparse = map.hasTrait() val serdeCtx = SerdeCtx("reader") @@ -541,14 +541,6 @@ open class XmlParserGenerator( val keyName = map.key.getTrait()?.value ?: map.key.memberName writeInline("#S -> key = ", keyName) deserializeMember(ctx, innerCtx, map.key, this) - // FIXME - We re-use deserializeMember here but key types targeting enums - // have to pull the raw string value back out because of - // https://github.com/awslabs/smithy-kotlin/issues/1045 - val targetValueShape = ctx.model.expectShape(map.key.target) - if (targetValueShape.type == ShapeType.ENUM) { - writer.indent() - .write(".value") - } val valueName = map.value.getTrait()?.value ?: map.value.memberName if (isSparse) { diff --git a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt index 07e248119..f1f980f8b 100644 --- a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt +++ b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt @@ -529,21 +529,58 @@ class SymbolProviderTest { assertEquals("Map", mapSymbol.name) - // collections should contain a reference to the member type - assertEquals("Record", mapSymbol.references[0].symbol.name) + // collections should contain a reference to the member types + val refNames = mapSymbol.references.map { it.symbol.fullName } + assertTrue("kotlin.String" in refNames) + assertTrue("com.test.model.Record" in refNames) val sparseMapSymbol = provider.toSymbol(model.expectShape("${TestModelDefault.NAMESPACE}#MySparseMap")) assertEquals("Map", sparseMapSymbol.name) // collections should contain a reference to the member type - assertEquals("Record", sparseMapSymbol.references[0].symbol.name) + val sparseRefNames = mapSymbol.references.map { it.symbol.fullName } + assertTrue("kotlin.String" in sparseRefNames) + assertTrue("com.test.model.Record" in sparseRefNames) // check the fully qualified name hint is set assertEquals("Map", mapSymbol.fullNameHint) assertEquals("Map", sparseMapSymbol.fullNameHint) } + @Test + fun `creates maps with enum keys`() { + val model = """ + @enum([ + { + value: "FOO", + }, + { + value: "BAR", + }, + ]) + string Type + + structure Record {} + + map MyMap { + key: Type, + value: Record, + } + """.prependNamespaceAndService().toSmithyModel() + + val provider: SymbolProvider = KotlinCodegenPlugin.createSymbolProvider(model) + + val mapSymbol = provider.toSymbol(model.expectShape("${TestModelDefault.NAMESPACE}#MyMap")) + + assertEquals("Map", mapSymbol.name) + + // collections should contain a reference to the member types + val refNames = mapSymbol.references.map { it.symbol.fullName } + assertTrue("com.test.model.Type" in refNames) + assertTrue("com.test.model.Record" in refNames) + } + @DisplayName("creates bigNumbers") @ParameterizedTest(name = "{index} ==> ''{0}''") @ValueSource(strings = ["BigInteger", "BigDecimal"]) diff --git a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGeneratorTest.kt b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGeneratorTest.kt index 8e6a738f1..101c3e950 100644 --- a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGeneratorTest.kt +++ b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGeneratorTest.kt @@ -50,6 +50,52 @@ mapOf( contents.shouldContainOnlyOnceWithDiff(expected) } + @Test + fun `it renders maps with enum keys`() { + val model = """ + @enum([ + { + value: "k1", + }, + { + value: "k2", + }, + { + value: "k3", + }, + ]) + string KeyType + + map MyMap { + key: KeyType, + value: Integer, + } + """.prependNamespaceAndService(namespace = "foo.bar").toSmithyModel() + + val provider: SymbolProvider = KotlinCodegenPlugin.createSymbolProvider(model, rootNamespace = "foo.bar") + val mapShape = model.expectShape(ShapeId.from("foo.bar#MyMap")) + val writer = KotlinWriter("test") + + val params = Node.objectNodeBuilder() + .withMember("k1", 1) + .withMember("k2", 2) + .withMember("k3", 3) + .build() + + ShapeValueGenerator(model, provider).instantiateShapeInline(writer, mapShape, params) + val contents = writer.toString() + + val expected = """ +mapOf( + KeyType.fromValue("k1") to 1, + KeyType.fromValue("k2") to 2, + KeyType.fromValue("k3") to 3 +) +""" + + contents.shouldContainOnlyOnceWithDiff(expected) + } + @Test fun `it renders lists`() { val model = """ diff --git a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGeneratorTest.kt b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGeneratorTest.kt index 0d0349f21..abd959dda 100644 --- a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGeneratorTest.kt +++ b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGeneratorTest.kt @@ -1026,6 +1026,57 @@ class DeserializeStructGeneratorTest { actual.shouldContainOnlyOnceWithDiff(expected) } + @Test + fun `it deserializes a structure containing a map with an enum key`() { + val model = ( + modelPrefix + """ + structure FooResponse { + payload: KeyValuedMap + } + + map KeyValuedMap { + key: KeyType, + value: String + } + + @enum([ + { + value: "FOO", + }, + { + value: "BAR", + }, + ]) + string KeyType + """ + ).toSmithyModel() + + val expected = """ + deserializer.deserializeStruct(OBJ_DESCRIPTOR) { + loop@while (true) { + when (findNextFieldIndex()) { + PAYLOAD_DESCRIPTOR.index -> builder.payload = + deserializer.deserializeMap(PAYLOAD_DESCRIPTOR) { + val map0 = mutableMapOf() + while (hasNextEntry()) { + val k0 = KeyType.fromValue(key()) + val v0 = if (nextHasValue()) { deserializeString() } else { deserializeNull(); continue } + map0[k0] = v0 + } + map0 + } + null -> break@loop + else -> skipValue() + } + } + } + """.trimIndent() + + val actual = codegenDeserializerForShape(model, "com.test#Foo") + + actual.shouldContainOnlyOnceWithDiff(expected) + } + @Test fun `it deserializes a structure containing a map of a union of primitive values`() { val model = ( diff --git a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGeneratorTest.kt b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGeneratorTest.kt index 066f2fef6..6c74c5e48 100644 --- a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGeneratorTest.kt +++ b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGeneratorTest.kt @@ -1034,6 +1034,46 @@ class SerializeStructGeneratorTest { actual.shouldContainOnlyOnceWithDiff(expected) } + @Test + fun `it serializes a structure containing a map with enum keys`() { + val model = ( + modelPrefix + """ + structure FooRequest { + payload: KeyValuedMap + } + + map KeyValuedMap { + key: KeyType, + value: String + } + + @enum([ + { + value: "FOO", + }, + { + value: "BAR", + }, + ]) + string KeyType + """ + ).toSmithyModel() + + val expected = """ + serializer.serializeStruct(OBJ_DESCRIPTOR) { + if (input.payload != null) { + mapField(PAYLOAD_DESCRIPTOR) { + input.payload.forEach { (key, value) -> entry(key.value, value) } + } + } + } + """.trimIndent() + + val actual = codegenSerializerForShape(model, "com.test#Foo").stripCodegenPrefix() + + actual.shouldContainOnlyOnceWithDiff(expected) + } + @Test fun `it serializes a structure containing a required map`() { val model = ( diff --git a/tests/codegen/serde-tests/src/test/kotlin/aws/smithy/kotlin/tests/serde/XmlMapTest.kt b/tests/codegen/serde-tests/src/test/kotlin/aws/smithy/kotlin/tests/serde/XmlMapTest.kt index 530df9186..0cd961424 100644 --- a/tests/codegen/serde-tests/src/test/kotlin/aws/smithy/kotlin/tests/serde/XmlMapTest.kt +++ b/tests/codegen/serde-tests/src/test/kotlin/aws/smithy/kotlin/tests/serde/XmlMapTest.kt @@ -249,9 +249,9 @@ class XmlMapTest : AbstractXmlTest() { // see also https://github.com/awslabs/smithy-kotlin/issues/1045 val expected = StructType { enumKeyMap = mapOf( - FooEnum.Foo.value to 1, - "Bar" to 2, - "Unknown" to 3, + FooEnum.Foo to 1, + FooEnum.Bar to 2, + FooEnum.SdkUnknown("Unknown") to 3, ) } val payload = """ diff --git a/tests/compile/src/test/resources/kitchen-sink-model.smithy b/tests/compile/src/test/resources/kitchen-sink-model.smithy index c1fb3747d..b5643d3fe 100644 --- a/tests/compile/src/test/resources/kitchen-sink-model.smithy +++ b/tests/compile/src/test/resources/kitchen-sink-model.smithy @@ -239,6 +239,11 @@ map StringMap { value: String } +map EnumKeyedStringMap { + key: MyEnum, + value: String +} + // only exists as value of a map through MapInputRequest::structMap structure ReachableOnlyThroughMap { prop1: Integer @@ -280,6 +285,8 @@ structure MapInputRequest { structMap: StructMap, enumMap: EnumMap, blobMap: BlobMap, + stringMap: StringMap, + enumKeyedStringMap: EnumKeyedStringMap, mapOfLists: MapOfLists, nestedMap: NestedMap } @@ -289,6 +296,8 @@ structure MapOutputResponse { structMap: StructMap, enumMap: EnumMap, blobMap: BlobMap, + stringMap: StringMap, + enumKeyedStringMap: EnumKeyedStringMap, nestedMap: NestedMap } From e7e20964e13b098e58b6cb80119d3cfb1e905ae0 Mon Sep 17 00:00:00 2001 From: Ian Botsford <83236726+ianbotsf@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:28:51 +0000 Subject: [PATCH 2/5] fix bug in tests --- .../amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt index f1f980f8b..0cc4d2cb1 100644 --- a/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt +++ b/codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/core/SymbolProviderTest.kt @@ -539,7 +539,7 @@ class SymbolProviderTest { assertEquals("Map", sparseMapSymbol.name) // collections should contain a reference to the member type - val sparseRefNames = mapSymbol.references.map { it.symbol.fullName } + val sparseRefNames = sparseMapSymbol.references.map { it.symbol.fullName } assertTrue("kotlin.String" in sparseRefNames) assertTrue("com.test.model.Record" in sparseRefNames) From 6aeecf8ea4aa39fe252c3a65f5650952038cd424 Mon Sep 17 00:00:00 2001 From: Ian Botsford <83236726+ianbotsf@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:03:33 +0000 Subject: [PATCH 3/5] include draft breaking change announcement --- BREAKING-CHANGE.tmp.md | 1280 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1280 insertions(+) create mode 100644 BREAKING-CHANGE.tmp.md diff --git a/BREAKING-CHANGE.tmp.md b/BREAKING-CHANGE.tmp.md new file mode 100644 index 000000000..8b42918d0 --- /dev/null +++ b/BREAKING-CHANGE.tmp.md @@ -0,0 +1,1280 @@ +# BREAKING: Map key changes + +An upcoming release of the **AWS SDK for Kotlin** will change the key type for several maps from strings into specific enumerations. + +# Release date + +This feature will ship with the **v1.0.80** release planned for **3/18/2024**. + +# What's changing + +Several members of service request/response types are maps which incorrectly use `String` as the key type. These types are being replaced to use a member-specific `enum class` as the key type. + +For example, `SqsClient` has an operation `setQueueAttributes` whose request object has a map member named `attributes`. Previously this member was of type `Map?` but now is of type `Map?` to more accurately reflect the expected key values. See the **Full list of updated maps** section below for a detailed list of all changes. + +# How to migrate + +If you are using any of the map members listed in the **Full list of updated maps** section below, you will need to update your code to change affected map keys from strings to a new enum type. + +For example, a prior call to SQS's `setQueueAttributes` operation may have looked like this: + +```kotlin +sqs.setQueueAttributes { + queueUrl = "..." + attributes = mapOf( + "MaximumMessageSize" to "1024", // 1KB + "VisibilityTimeout" to "3600", // 1 hour + ) +} +``` + +After the breaking change, the equivalent code would be: + +```kotlin +sqs.setQueueAttributes { + queueUrl = "..." + attributes = mapOf( + QueueAttributeName.MaximumMessageSize to "1024", // 1KB + QueueAttributeName.VisibilityTimeout to "3600", // 1 hour + ) +} +``` + +## Handling unmodeled key values + +If your code was using a string constant which is not represented as an enum element, you will need to update your code to use the `SdkUnknown` enum variant. Take for instance prior code which interacted with a map like this: + +```kotlin +mapOf( + "Foo" to 1, + "Bar" to 2, + "SomeOtherName" to 999, +) +``` + +If the string key is replaced with a new enum type `Keys` which has elements `Foo` and `Bar` but not `SomeOtherName` then the equivalent code would be: + +```kotlin +mapOf( + Keys.Foo to 1, + Keys.Bar to 2, + Keys.SdkUnknown("SomeOtherName") to 999, +) +``` + +This is a rare use case. Unexpected key values may already be ignored by the service or cause an exception. + +# Full list of updated maps + +The following services have request/response types in which a map has changed type: + +## AppConfig + +* `CreateExtensionRequest.actions` + * Old type: `Map>` + * New type: `Map>` + * Operation: `createExtension` +* `Extension.actions` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createExtension` + * `getExtension` + * `updateExtension` +* `UpdateExtensionRequest.actions` + * Old type: `Map>` + * New type: `Map>` + * Operation: `updateExtension` + +## Appflow + +* `DescribeConnectorsResponse.connectorConfigurations` + * Old type: `Map` + * New type: `Map` + * Operation: `describeConnectors` +* `Task.taskProperties` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createFlow` + * `describeFlow` + * `updateFlow` + +## Application Insights + +* `ApplicationComponent.detectedWorkload` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `listComponents` + * `describeComponent` +* `Problem.feedback` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeProblem` + * `listProblems` + +## Backup + +* `CopyJob.childJobsInState` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listCopyJobs` + * `describeCopyJob` +* `DescribeBackupJobOutput.childJobsInState` + * Old type: `Map` + * New type: `Map` + * Operation: `describeBackupJob` + +## CodeDeploy + +* `ListDeploymentTargetsInput.targetFilters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `listDeploymentTargets` + +## CodeGuruProfiler + +* `AgentConfiguration.agentParameters` + * Old type: `Map` + * New type: `Map` + * Operation: `configureAgent` +* `ConfigureAgentRequest.metadata` + * Old type: `Map` + * New type: `Map` + * Operation: `configureAgent` + +## Codeartifact + +* `AssetSummary.hashes` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listPackageVersionAssets` + * `publishPackageVersion` + +## Connect + +* `UserData.activeSlotsByChannel` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getCurrentUserData` + * `getCurrentUserData` + * `getCurrentUserData` +* `UserData.availableSlotsByChannel` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getCurrentUserData` + * `getCurrentUserData` + * `getCurrentUserData` +* `UserData.maxSlotsByChannel` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getCurrentUserData` + * `getCurrentUserData` + * `getCurrentUserData` + +## Customer Profiles + +* `Task.taskProperties` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createIntegrationWorkflow` + * `putIntegration` + +## DataSync + +* `DescribeStorageSystemResourcesRequest.filter` + * Old type: `Map>` + * New type: `Map>` + * Operation: `describeStorageSystemResources` + +## Detective + +* `DatasourcePackageIngestDetail.lastIngestStateChange` + * Old type: `Map` + * New type: `Map` + * Operation: `listDatasourcePackages` +* `ListDatasourcePackagesResponse.datasourcePackages` + * Old type: `Map` + * New type: `Map` + * Operation: `listDatasourcePackages` +* `MemberDetail.datasourcePackageIngestStates` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createMembers` + * `getMembers` + * `listInvitations` + * `listMembers` +* `MemberDetail.volumeUsageByDatasourcePackage` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createMembers` + * `getMembers` + * `listInvitations` + * `listMembers` +* `MembershipDatasources.datasourcePackageIngestHistory` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `batchGetGraphMemberDatasources` + * `batchGetMembershipDatasources` +* `MembershipDatasources.datasourcePackageIngestHistory` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `batchGetGraphMemberDatasources` + * `batchGetMembershipDatasources` + +## Device Farm + +* `AccountSettings.unmeteredDevices` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getAccountSettings` + * `getAccountSettings` +* `AccountSettings.unmeteredRemoteAccessDevices` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getAccountSettings` + * `getAccountSettings` +* `ListUniqueProblemsResult.uniqueProblems` + * Old type: `Map>` + * New type: `Map>` + * Operation: `listUniqueProblems` + +## ECR + +* `ImageScanFindings.findingSeverityCounts` + * Old type: `Map` + * New type: `Map` + * Operation: `describeImageScanFindings` +* `ImageScanFindingsSummary.findingSeverityCounts` + * Old type: `Map` + * New type: `Map` + * Operation: `describeImages` + +## Elasticsearch Service + +* `CreateElasticsearchDomainRequest.logPublishingOptions` + * Old type: `Map` + * New type: `Map` + * Operation: `createElasticsearchDomain` +* `ElasticsearchDomainStatus.logPublishingOptions` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createElasticsearchDomain` + * `deleteElasticsearchDomain` + * `describeElasticsearchDomain` + * `describeElasticsearchDomains` +* `LogPublishingOptionsStatus.options` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeElasticsearchDomainConfig` + * `updateElasticsearchDomainConfig` +* `UpdateElasticsearchDomainConfigRequest.logPublishingOptions` + * Old type: `Map` + * New type: `Map` + * Operation: `updateElasticsearchDomainConfig` + +## FMS + +* `Policy.excludeMap` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `getPolicy` + * `putPolicy` + * `putPolicy` + * `getPolicy` + * `putPolicy` + * `putPolicy` +* `Policy.includeMap` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `getPolicy` + * `putPolicy` + * `putPolicy` + * `getPolicy` + * `putPolicy` + * `putPolicy` +* `PolicyComplianceDetail.issueInfoMap` + * Old type: `Map` + * New type: `Map` + * Operation: `getComplianceDetail` +* `PolicyComplianceStatus.issueInfoMap` + * Old type: `Map` + * New type: `Map` + * Operation: `listComplianceStatus` + +## Glue + +* `Connection.connectionProperties` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getConnections` + * `getConnection` +* `ConnectionInput.connectionProperties` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createConnection` + * `updateConnection` +* `EvaluateDataQualityMultiFrame.additionalOptions` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createJob` + * `getJob` + * `batchGetJobs` + * `getJobs` + * `updateJob` +* `JdbcConnectorOptions.dataTypeMapping` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createJob` + * `getJob` + * `batchGetJobs` + * `getJobs` + * `updateJob` + +## GuardDuty + +* `CoverageStatistics.countByCoverageStatus` + * Old type: `Map` + * New type: `Map` + * Operation: `getCoverageStatistics` +* `CoverageStatistics.countByResourceType` + * Old type: `Map` + * New type: `Map` + * Operation: `getCoverageStatistics` +* `ScanResourceCriteria.exclude` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getMalwareScanSettings` + * `updateMalwareScanSettings` + * `getMalwareScanSettings` + * `updateMalwareScanSettings` +* `ScanResourceCriteria.include` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getMalwareScanSettings` + * `updateMalwareScanSettings` + * `getMalwareScanSettings` + * `updateMalwareScanSettings` + +## Health + +* `AccountEntityAggregate.statuses` + * Old type: `Map` + * New type: `Map` + * Operation: `describeEntityAggregatesForOrganization` +* `EntityAggregate.statuses` + * Old type: `Map` + * New type: `Map` + * Operation: `describeEntityAggregates` +* `OrganizationEntityAggregate.statuses` + * Old type: `Map` + * New type: `Map` + * Operation: `describeEntityAggregatesForOrganization` + +## IAM + +* `GetAccountSummaryResponse.summaryMap` + * Old type: `Map` + * New type: `Map` + * Operation: `getAccountSummary` + +## Inspector + +* `AssessmentRun.findingCounts` + * Old type: `Map` + * New type: `Map` + * Operation: `describeAssessmentRuns` + +## IoT + +* `CreateSecurityProfileRequest.alertTargets` + * Old type: `Map` + * New type: `Map` + * Operation: `createSecurityProfile` +* `DescribeAccountAuditConfigurationResponse.auditNotificationTargetConfigurations` + * Old type: `Map` + * New type: `Map` + * Operation: `describeAccountAuditConfiguration` +* `DescribeEventConfigurationsResponse.eventConfigurations` + * Old type: `Map` + * New type: `Map` + * Operation: `describeEventConfigurations` +* `DescribeSecurityProfileResponse.alertTargets` + * Old type: `Map` + * New type: `Map` + * Operation: `describeSecurityProfile` +* `UpdateAccountAuditConfigurationRequest.auditNotificationTargetConfigurations` + * Old type: `Map` + * New type: `Map` + * Operation: `updateAccountAuditConfiguration` +* `UpdateEventConfigurationsRequest.eventConfigurations` + * Old type: `Map` + * New type: `Map` + * Operation: `updateEventConfigurations` +* `UpdateSecurityProfileRequest.alertTargets` + * Old type: `Map` + * New type: `Map` + * Operation: `updateSecurityProfile` +* `UpdateSecurityProfileResponse.alertTargets` + * Old type: `Map` + * New type: `Map` + * Operation: `updateSecurityProfile` + +## Kinesis Video + +* `ImageGenerationConfiguration.formatConfig` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeImageGenerationConfiguration` + * `updateImageGenerationConfiguration` + +## Kinesis Video Archived Media + +* `GetImagesInput.formatConfig` + * Old type: `Map` + * New type: `Map` + * Operation: `getImages` + +## LakeFormation + +* `UpdateTableStorageOptimizerRequest.storageOptimizerConfig` + * Old type: `Map>` + * New type: `Map>` + * Operation: `updateTableStorageOptimizer` + +## Lambda + +* `SelfManagedEventSource.endpoints` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createEventSourceMapping` + * `createEventSourceMapping` + * `deleteEventSourceMapping` + * `listEventSourceMappings` + * `getEventSourceMapping` + * `updateEventSourceMapping` + +## Lex Models V2 + +* `IntentClassificationTestResultItemCounts.intentMatchResultCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listTestExecutionResultItems` + * `listTestExecutionResultItems` +* `IntentClassificationTestResultItemCounts.speechTranscriptionResultCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listTestExecutionResultItems` + * `listTestExecutionResultItems` +* `OverallTestResultItem.endToEndResultCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listTestExecutionResultItems` + * `listTestExecutionResultItems` +* `OverallTestResultItem.speechTranscriptionResultCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listTestExecutionResultItems` + * `listTestExecutionResultItems` +* `PromptSpecification.promptAttemptsSpecification` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createIntent` + * `createIntent` + * `describeIntent` + * `updateIntent` + * `updateIntent` + * `listSlots` + * `createSlot` + * `createSlot` + * `describeSlot` + * `updateSlot` + * `updateSlot` + * `createSlot` + * `createSlot` + * `describeSlot` + * `updateSlot` + * `updateSlot` +* `SlotResolutionTestResultItemCounts.slotMatchResultCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listTestExecutionResultItems` + * `listTestExecutionResultItems` +* `SlotResolutionTestResultItemCounts.speechTranscriptionResultCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listTestExecutionResultItems` + * `listTestExecutionResultItems` + +## Lightsail + +* `LoadBalancer.configurationOptions` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getLoadBalancer` + * `getLoadBalancers` + +## Machine Learning + +* `Prediction.details` + * Old type: `Map` + * New type: `Map` + * Operation: `predict` + +## Marketplace Entitlement Service + +* `GetEntitlementsRequest.filter` + * Old type: `Map>` + * New type: `Map>` + * Operation: `getEntitlements` + +## Omics + +* `TsvStoreOptions.formatToHeader` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createAnnotationStore` + * `createAnnotationStore` + * `getAnnotationStore` + * `updateAnnotationStore` +* `TsvVersionOptions.formatToHeader` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createAnnotationStoreVersion` + * `createAnnotationStoreVersion` + * `getAnnotationStoreVersion` + +## OpenSearch + +* `CreateDomainRequest.logPublishingOptions` + * Old type: `Map` + * New type: `Map` + * Operation: `createDomain` +* `DomainStatus.logPublishingOptions` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createDomain` + * `deleteDomain` + * `describeDomain` + * `describeDryRunProgress` + * `describeDomains` +* `LogPublishingOptionsStatus.options` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeDomainConfig` + * `updateDomainConfig` +* `UpdateDomainConfigRequest.logPublishingOptions` + * Old type: `Map` + * New type: `Map` + * Operation: `updateDomainConfig` + +## OpsWorks + +* `App.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `describeApps` +* `CloneStackRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `cloneStack` +* `CreateAppRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `createApp` +* `CreateLayerRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `createLayer` +* `CreateStackRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `createStack` +* `Layer.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `describeLayers` +* `Stack.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `describeStacks` +* `UpdateAppRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `updateApp` +* `UpdateLayerRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `updateLayer` +* `UpdateStackRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `updateStack` + +## Outposts + +* `OrderSummary.lineItemCountsByStatus` + * Old type: `Map` + * New type: `Map` + * Operation: `listOrders` + +## Pinpoint + +* `OpenHours.custom` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` +* `OpenHours.email` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` +* `OpenHours.push` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` +* `OpenHours.sms` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` +* `OpenHours.voice` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + * `createJourney` + * `deleteJourney` + * `getJourney` + * `listJourneys` + * `updateJourney` + * `updateJourneyState` + * `createJourney` + * `updateJourney` + +## Pinpoint SMS Voice V2 + +* `SendDestinationNumberVerificationCodeRequest.destinationCountryParameters` + * Old type: `Map` + * New type: `Map` + * Operation: `sendDestinationNumberVerificationCode` +* `SendTextMessageRequest.destinationCountryParameters` + * Old type: `Map` + * New type: `Map` + * Operation: `sendTextMessage` + +## PrivateNetworks + +* `ListDeviceIdentifiersRequest.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `listDeviceIdentifiers` +* `ListNetworkResourcesRequest.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `listNetworkResources` +* `ListNetworkSitesRequest.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `listNetworkSites` +* `ListNetworksRequest.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `listNetworks` +* `ListOrdersRequest.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `listOrders` + +## Resiliencehub + +* `AppAssessment.compliance` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeAppAssessment` + * `startAppAssessment` +* `AppComponentCompliance.compliance` + * Old type: `Map` + * New type: `Map` + * Operation: `listAppComponentCompliances` +* `ComplianceDrift.actualValue` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listAppAssessmentComplianceDrifts` + * `listAppAssessmentComplianceDrifts` +* `ComplianceDrift.expectedValue` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listAppAssessmentComplianceDrifts` + * `listAppAssessmentComplianceDrifts` +* `ConfigRecommendation.compliance` + * Old type: `Map` + * New type: `Map` + * Operation: `listAppComponentRecommendations` +* `ConfigRecommendation.recommendationCompliance` + * Old type: `Map` + * New type: `Map` + * Operation: `listAppComponentRecommendations` +* `CreateResiliencyPolicyRequest.policy` + * Old type: `Map` + * New type: `Map` + * Operation: `createResiliencyPolicy` +* `ResiliencyPolicy.policy` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeAppAssessment` + * `startAppAssessment` + * `createResiliencyPolicy` + * `describeResiliencyPolicy` + * `listResiliencyPolicies` + * `listSuggestedResiliencyPolicies` + * `updateResiliencyPolicy` +* `ResiliencyScore.componentScore` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeAppAssessment` + * `startAppAssessment` + * `listAppComponentCompliances` +* `ResiliencyScore.disruptionScore` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeAppAssessment` + * `startAppAssessment` + * `listAppComponentCompliances` +* `UpdateResiliencyPolicyRequest.policy` + * Old type: `Map` + * New type: `Map` + * Operation: `updateResiliencyPolicy` + +## SESv2 + +* `BatchGetMetricDataQuery.dimensions` + * Old type: `Map` + * New type: `Map` + * Operation: `batchGetMetricData` +* `ListRecommendationsRequest.filter` + * Old type: `Map` + * New type: `Map` + * Operation: `listRecommendations` +* `MetricsDataSource.dimensions` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createExportJob` + * `getExportJob` + +## SQS + +* `CreateQueueRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `createQueue` +* `GetQueueAttributesResult.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `getQueueAttributes` +* `Message.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `receiveMessage` +* `SendMessageBatchRequestEntry.messageSystemAttributes` + * Old type: `Map` + * New type: `Map` + * Operation: `sendMessageBatch` +* `SendMessageRequest.messageSystemAttributes` + * Old type: `Map` + * New type: `Map` + * Operation: `sendMessage` +* `SetQueueAttributesRequest.attributes` + * Old type: `Map` + * New type: `Map` + * Operation: `setQueueAttributes` + +## SSM Contacts + +* `RecurrenceSettings.shiftCoverages` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createRotation` + * `getRotation` + * `listPreviewRotationShifts` + * `listRotations` + * `updateRotation` + +## SageMaker + +* `AutoMlCandidate.inferenceContainerDefinitions` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `listCandidatesForAutoMlJob` + * `describeAutoMlJob` + * `describeAutoMlJobV2` +* `TimeSeriesTransformations.filling` + * Old type: `Map>` + * New type: `Map>` + * Operations: + * `createAutoMlJobV2` + * `describeAutoMlJobV2` + +## Service Catalog + +* `CopyProductInput.sourceProvisioningArtifactIdentifiers` + * Old type: `List>` + * New type: `List>` + * Operation: `copyProduct` +* `CreateServiceActionInput.definition` + * Old type: `Map` + * New type: `Map` + * Operation: `createServiceAction` +* `SearchProductsAsAdminInput.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `searchProductsAsAdmin` +* `SearchProductsInput.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `searchProducts` +* `SearchProvisionedProductsInput.filters` + * Old type: `Map>` + * New type: `Map>` + * Operation: `searchProvisionedProducts` +* `ServiceActionDetail.definition` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createServiceAction` + * `describeServiceAction` + * `updateServiceAction` +* `UpdateProvisionedProductPropertiesInput.provisionedProductProperties` + * Old type: `Map` + * New type: `Map` + * Operation: `updateProvisionedProductProperties` +* `UpdateProvisionedProductPropertiesOutput.provisionedProductProperties` + * Old type: `Map` + * New type: `Map` + * Operation: `updateProvisionedProductProperties` +* `UpdateServiceActionInput.definition` + * Old type: `Map` + * New type: `Map` + * Operation: `updateServiceAction` + +## ServiceDiscovery + +* `Operation.targets` + * Old type: `Map` + * New type: `Map` + * Operation: `getOperation` + +## Transcribe + +* `CallAnalyticsJobSettings.languageIdSettings` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getCallAnalyticsJob` + * `startCallAnalyticsJob` + * `startCallAnalyticsJob` +* `StartTranscriptionJobRequest.languageIdSettings` + * Old type: `Map` + * New type: `Map` + * Operation: `startTranscriptionJob` +* `TranscriptionJob.languageIdSettings` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getTranscriptionJob` + * `startTranscriptionJob` + +## WAFV2 + +* `AssociationConfig.requestBody` + * Old type: `Map` + * New type: `Map` + * Operations: + * `createWebAcl` + * `updateWebAcl` + * `getWebAclForResource` + * `getWebAcl` + +## WellArchitected + +* `CheckSummary.accountSummary` + * Old type: `Map` + * New type: `Map` + * Operation: `listCheckSummaries` +* `ConsolidatedReportMetric.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operation: `getConsolidatedReport` +* `LensMetric.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operation: `getConsolidatedReport` +* `LensReview.prioritizedRiskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getLensReview` + * `updateLensReview` + * `getLensReview` + * `updateLensReview` +* `LensReview.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getLensReview` + * `updateLensReview` + * `getLensReview` + * `updateLensReview` +* `LensReviewSummary.prioritizedRiskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listLensReviews` + * `listLensReviews` +* `LensReviewSummary.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listLensReviews` + * `listLensReviews` +* `PillarMetric.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operation: `getConsolidatedReport` +* `PillarReviewSummary.prioritizedRiskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getLensReview` + * `updateLensReview` + * `getLensReview` + * `updateLensReview` +* `PillarReviewSummary.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getLensReview` + * `updateLensReview` + * `getLensReview` + * `updateLensReview` +* `ReviewTemplate.questionCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getReviewTemplate` + * `updateReviewTemplate` +* `ReviewTemplateLensReview.questionCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getReviewTemplateLensReview` + * `updateReviewTemplateLensReview` +* `ReviewTemplatePillarReviewSummary.questionCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getReviewTemplateLensReview` + * `updateReviewTemplateLensReview` +* `Workload.prioritizedRiskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getWorkload` + * `getMilestone` + * `updateWorkload` + * `getWorkload` + * `getMilestone` + * `updateWorkload` +* `Workload.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `getWorkload` + * `getMilestone` + * `updateWorkload` + * `getWorkload` + * `getMilestone` + * `updateWorkload` +* `WorkloadSummary.prioritizedRiskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listMilestones` + * `listWorkloads` + * `listMilestones` + * `listWorkloads` +* `WorkloadSummary.riskCounts` + * Old type: `Map` + * New type: `Map` + * Operations: + * `listMilestones` + * `listWorkloads` + * `listMilestones` + * `listWorkloads` + +## WorkDocs + +* `DocumentVersionMetadata.source` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeFolderContents` + * `getResources` + * `getDocument` + * `initiateDocumentVersionUpload` + * `searchResources` + * `describeDocumentVersions` + * `getDocumentVersion` + * `searchResources` +* `DocumentVersionMetadata.thumbnail` + * Old type: `Map` + * New type: `Map` + * Operations: + * `describeFolderContents` + * `getResources` + * `getDocument` + * `initiateDocumentVersionUpload` + * `searchResources` + * `describeDocumentVersions` + * `getDocumentVersion` + * `searchResources` + +# Feedback + +If you have any questions concerning this change, please feel free to engage with us in this discussion. If you encounter a bug with these changes, please [file an issue](https://github.com/awslabs/aws-sdk-kotlin/issues/new/choose). From 4cd52d57cd32834ac4ae11fe98a797642d7289f1 Mon Sep 17 00:00:00 2001 From: Ian Botsford <83236726+ianbotsf@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:13:19 +0000 Subject: [PATCH 4/5] correct breaking version number --- BREAKING-CHANGE.tmp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BREAKING-CHANGE.tmp.md b/BREAKING-CHANGE.tmp.md index 8b42918d0..d6ea11410 100644 --- a/BREAKING-CHANGE.tmp.md +++ b/BREAKING-CHANGE.tmp.md @@ -4,7 +4,7 @@ An upcoming release of the **AWS SDK for Kotlin** will change the key type for s # Release date -This feature will ship with the **v1.0.80** release planned for **3/18/2024**. +This feature will ship with the **v1.1.0** release planned for **3/18/2024**. # What's changing From e614dda9ccb833c694c988f8d0693320667177ce Mon Sep 17 00:00:00 2001 From: Ian Botsford <83236726+ianbotsf@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:43:03 +0000 Subject: [PATCH 5/5] updating changelog to final revision, removing temporary breaking change announcement --- .../a7f82a33-11f1-4184-97af-ff713e922dfc.json | 2 +- BREAKING-CHANGE.tmp.md | 1280 ----------------- 2 files changed, 1 insertion(+), 1281 deletions(-) delete mode 100644 BREAKING-CHANGE.tmp.md diff --git a/.changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json b/.changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json index 6e4e756a5..8697c4d46 100644 --- a/.changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json +++ b/.changes/a7f82a33-11f1-4184-97af-ff713e922dfc.json @@ -1,7 +1,7 @@ { "id": "a7f82a33-11f1-4184-97af-ff713e922dfc", "type": "bugfix", - "description": "⚠️ **IMPORTANT**: Fix codegen for map shapes which use string enums as map keys. See the Map Enum Keys breaking change announcement (link coming soon) for more details", + "description": "⚠️ **IMPORTANT**: Fix codegen for map shapes which use string enums as map keys. See the [**Map key changes** breaking change announcement](https://github.com/awslabs/aws-sdk-kotlin/discussions/1258) for more details", "issues": [ "awslabs/smithy-kotlin#1045" ], diff --git a/BREAKING-CHANGE.tmp.md b/BREAKING-CHANGE.tmp.md deleted file mode 100644 index d6ea11410..000000000 --- a/BREAKING-CHANGE.tmp.md +++ /dev/null @@ -1,1280 +0,0 @@ -# BREAKING: Map key changes - -An upcoming release of the **AWS SDK for Kotlin** will change the key type for several maps from strings into specific enumerations. - -# Release date - -This feature will ship with the **v1.1.0** release planned for **3/18/2024**. - -# What's changing - -Several members of service request/response types are maps which incorrectly use `String` as the key type. These types are being replaced to use a member-specific `enum class` as the key type. - -For example, `SqsClient` has an operation `setQueueAttributes` whose request object has a map member named `attributes`. Previously this member was of type `Map?` but now is of type `Map?` to more accurately reflect the expected key values. See the **Full list of updated maps** section below for a detailed list of all changes. - -# How to migrate - -If you are using any of the map members listed in the **Full list of updated maps** section below, you will need to update your code to change affected map keys from strings to a new enum type. - -For example, a prior call to SQS's `setQueueAttributes` operation may have looked like this: - -```kotlin -sqs.setQueueAttributes { - queueUrl = "..." - attributes = mapOf( - "MaximumMessageSize" to "1024", // 1KB - "VisibilityTimeout" to "3600", // 1 hour - ) -} -``` - -After the breaking change, the equivalent code would be: - -```kotlin -sqs.setQueueAttributes { - queueUrl = "..." - attributes = mapOf( - QueueAttributeName.MaximumMessageSize to "1024", // 1KB - QueueAttributeName.VisibilityTimeout to "3600", // 1 hour - ) -} -``` - -## Handling unmodeled key values - -If your code was using a string constant which is not represented as an enum element, you will need to update your code to use the `SdkUnknown` enum variant. Take for instance prior code which interacted with a map like this: - -```kotlin -mapOf( - "Foo" to 1, - "Bar" to 2, - "SomeOtherName" to 999, -) -``` - -If the string key is replaced with a new enum type `Keys` which has elements `Foo` and `Bar` but not `SomeOtherName` then the equivalent code would be: - -```kotlin -mapOf( - Keys.Foo to 1, - Keys.Bar to 2, - Keys.SdkUnknown("SomeOtherName") to 999, -) -``` - -This is a rare use case. Unexpected key values may already be ignored by the service or cause an exception. - -# Full list of updated maps - -The following services have request/response types in which a map has changed type: - -## AppConfig - -* `CreateExtensionRequest.actions` - * Old type: `Map>` - * New type: `Map>` - * Operation: `createExtension` -* `Extension.actions` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createExtension` - * `getExtension` - * `updateExtension` -* `UpdateExtensionRequest.actions` - * Old type: `Map>` - * New type: `Map>` - * Operation: `updateExtension` - -## Appflow - -* `DescribeConnectorsResponse.connectorConfigurations` - * Old type: `Map` - * New type: `Map` - * Operation: `describeConnectors` -* `Task.taskProperties` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createFlow` - * `describeFlow` - * `updateFlow` - -## Application Insights - -* `ApplicationComponent.detectedWorkload` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `listComponents` - * `describeComponent` -* `Problem.feedback` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeProblem` - * `listProblems` - -## Backup - -* `CopyJob.childJobsInState` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listCopyJobs` - * `describeCopyJob` -* `DescribeBackupJobOutput.childJobsInState` - * Old type: `Map` - * New type: `Map` - * Operation: `describeBackupJob` - -## CodeDeploy - -* `ListDeploymentTargetsInput.targetFilters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `listDeploymentTargets` - -## CodeGuruProfiler - -* `AgentConfiguration.agentParameters` - * Old type: `Map` - * New type: `Map` - * Operation: `configureAgent` -* `ConfigureAgentRequest.metadata` - * Old type: `Map` - * New type: `Map` - * Operation: `configureAgent` - -## Codeartifact - -* `AssetSummary.hashes` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listPackageVersionAssets` - * `publishPackageVersion` - -## Connect - -* `UserData.activeSlotsByChannel` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getCurrentUserData` - * `getCurrentUserData` - * `getCurrentUserData` -* `UserData.availableSlotsByChannel` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getCurrentUserData` - * `getCurrentUserData` - * `getCurrentUserData` -* `UserData.maxSlotsByChannel` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getCurrentUserData` - * `getCurrentUserData` - * `getCurrentUserData` - -## Customer Profiles - -* `Task.taskProperties` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createIntegrationWorkflow` - * `putIntegration` - -## DataSync - -* `DescribeStorageSystemResourcesRequest.filter` - * Old type: `Map>` - * New type: `Map>` - * Operation: `describeStorageSystemResources` - -## Detective - -* `DatasourcePackageIngestDetail.lastIngestStateChange` - * Old type: `Map` - * New type: `Map` - * Operation: `listDatasourcePackages` -* `ListDatasourcePackagesResponse.datasourcePackages` - * Old type: `Map` - * New type: `Map` - * Operation: `listDatasourcePackages` -* `MemberDetail.datasourcePackageIngestStates` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createMembers` - * `getMembers` - * `listInvitations` - * `listMembers` -* `MemberDetail.volumeUsageByDatasourcePackage` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createMembers` - * `getMembers` - * `listInvitations` - * `listMembers` -* `MembershipDatasources.datasourcePackageIngestHistory` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `batchGetGraphMemberDatasources` - * `batchGetMembershipDatasources` -* `MembershipDatasources.datasourcePackageIngestHistory` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `batchGetGraphMemberDatasources` - * `batchGetMembershipDatasources` - -## Device Farm - -* `AccountSettings.unmeteredDevices` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getAccountSettings` - * `getAccountSettings` -* `AccountSettings.unmeteredRemoteAccessDevices` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getAccountSettings` - * `getAccountSettings` -* `ListUniqueProblemsResult.uniqueProblems` - * Old type: `Map>` - * New type: `Map>` - * Operation: `listUniqueProblems` - -## ECR - -* `ImageScanFindings.findingSeverityCounts` - * Old type: `Map` - * New type: `Map` - * Operation: `describeImageScanFindings` -* `ImageScanFindingsSummary.findingSeverityCounts` - * Old type: `Map` - * New type: `Map` - * Operation: `describeImages` - -## Elasticsearch Service - -* `CreateElasticsearchDomainRequest.logPublishingOptions` - * Old type: `Map` - * New type: `Map` - * Operation: `createElasticsearchDomain` -* `ElasticsearchDomainStatus.logPublishingOptions` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createElasticsearchDomain` - * `deleteElasticsearchDomain` - * `describeElasticsearchDomain` - * `describeElasticsearchDomains` -* `LogPublishingOptionsStatus.options` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeElasticsearchDomainConfig` - * `updateElasticsearchDomainConfig` -* `UpdateElasticsearchDomainConfigRequest.logPublishingOptions` - * Old type: `Map` - * New type: `Map` - * Operation: `updateElasticsearchDomainConfig` - -## FMS - -* `Policy.excludeMap` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `getPolicy` - * `putPolicy` - * `putPolicy` - * `getPolicy` - * `putPolicy` - * `putPolicy` -* `Policy.includeMap` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `getPolicy` - * `putPolicy` - * `putPolicy` - * `getPolicy` - * `putPolicy` - * `putPolicy` -* `PolicyComplianceDetail.issueInfoMap` - * Old type: `Map` - * New type: `Map` - * Operation: `getComplianceDetail` -* `PolicyComplianceStatus.issueInfoMap` - * Old type: `Map` - * New type: `Map` - * Operation: `listComplianceStatus` - -## Glue - -* `Connection.connectionProperties` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getConnections` - * `getConnection` -* `ConnectionInput.connectionProperties` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createConnection` - * `updateConnection` -* `EvaluateDataQualityMultiFrame.additionalOptions` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createJob` - * `getJob` - * `batchGetJobs` - * `getJobs` - * `updateJob` -* `JdbcConnectorOptions.dataTypeMapping` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createJob` - * `getJob` - * `batchGetJobs` - * `getJobs` - * `updateJob` - -## GuardDuty - -* `CoverageStatistics.countByCoverageStatus` - * Old type: `Map` - * New type: `Map` - * Operation: `getCoverageStatistics` -* `CoverageStatistics.countByResourceType` - * Old type: `Map` - * New type: `Map` - * Operation: `getCoverageStatistics` -* `ScanResourceCriteria.exclude` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getMalwareScanSettings` - * `updateMalwareScanSettings` - * `getMalwareScanSettings` - * `updateMalwareScanSettings` -* `ScanResourceCriteria.include` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getMalwareScanSettings` - * `updateMalwareScanSettings` - * `getMalwareScanSettings` - * `updateMalwareScanSettings` - -## Health - -* `AccountEntityAggregate.statuses` - * Old type: `Map` - * New type: `Map` - * Operation: `describeEntityAggregatesForOrganization` -* `EntityAggregate.statuses` - * Old type: `Map` - * New type: `Map` - * Operation: `describeEntityAggregates` -* `OrganizationEntityAggregate.statuses` - * Old type: `Map` - * New type: `Map` - * Operation: `describeEntityAggregatesForOrganization` - -## IAM - -* `GetAccountSummaryResponse.summaryMap` - * Old type: `Map` - * New type: `Map` - * Operation: `getAccountSummary` - -## Inspector - -* `AssessmentRun.findingCounts` - * Old type: `Map` - * New type: `Map` - * Operation: `describeAssessmentRuns` - -## IoT - -* `CreateSecurityProfileRequest.alertTargets` - * Old type: `Map` - * New type: `Map` - * Operation: `createSecurityProfile` -* `DescribeAccountAuditConfigurationResponse.auditNotificationTargetConfigurations` - * Old type: `Map` - * New type: `Map` - * Operation: `describeAccountAuditConfiguration` -* `DescribeEventConfigurationsResponse.eventConfigurations` - * Old type: `Map` - * New type: `Map` - * Operation: `describeEventConfigurations` -* `DescribeSecurityProfileResponse.alertTargets` - * Old type: `Map` - * New type: `Map` - * Operation: `describeSecurityProfile` -* `UpdateAccountAuditConfigurationRequest.auditNotificationTargetConfigurations` - * Old type: `Map` - * New type: `Map` - * Operation: `updateAccountAuditConfiguration` -* `UpdateEventConfigurationsRequest.eventConfigurations` - * Old type: `Map` - * New type: `Map` - * Operation: `updateEventConfigurations` -* `UpdateSecurityProfileRequest.alertTargets` - * Old type: `Map` - * New type: `Map` - * Operation: `updateSecurityProfile` -* `UpdateSecurityProfileResponse.alertTargets` - * Old type: `Map` - * New type: `Map` - * Operation: `updateSecurityProfile` - -## Kinesis Video - -* `ImageGenerationConfiguration.formatConfig` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeImageGenerationConfiguration` - * `updateImageGenerationConfiguration` - -## Kinesis Video Archived Media - -* `GetImagesInput.formatConfig` - * Old type: `Map` - * New type: `Map` - * Operation: `getImages` - -## LakeFormation - -* `UpdateTableStorageOptimizerRequest.storageOptimizerConfig` - * Old type: `Map>` - * New type: `Map>` - * Operation: `updateTableStorageOptimizer` - -## Lambda - -* `SelfManagedEventSource.endpoints` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createEventSourceMapping` - * `createEventSourceMapping` - * `deleteEventSourceMapping` - * `listEventSourceMappings` - * `getEventSourceMapping` - * `updateEventSourceMapping` - -## Lex Models V2 - -* `IntentClassificationTestResultItemCounts.intentMatchResultCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listTestExecutionResultItems` - * `listTestExecutionResultItems` -* `IntentClassificationTestResultItemCounts.speechTranscriptionResultCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listTestExecutionResultItems` - * `listTestExecutionResultItems` -* `OverallTestResultItem.endToEndResultCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listTestExecutionResultItems` - * `listTestExecutionResultItems` -* `OverallTestResultItem.speechTranscriptionResultCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listTestExecutionResultItems` - * `listTestExecutionResultItems` -* `PromptSpecification.promptAttemptsSpecification` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createIntent` - * `createIntent` - * `describeIntent` - * `updateIntent` - * `updateIntent` - * `listSlots` - * `createSlot` - * `createSlot` - * `describeSlot` - * `updateSlot` - * `updateSlot` - * `createSlot` - * `createSlot` - * `describeSlot` - * `updateSlot` - * `updateSlot` -* `SlotResolutionTestResultItemCounts.slotMatchResultCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listTestExecutionResultItems` - * `listTestExecutionResultItems` -* `SlotResolutionTestResultItemCounts.speechTranscriptionResultCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listTestExecutionResultItems` - * `listTestExecutionResultItems` - -## Lightsail - -* `LoadBalancer.configurationOptions` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getLoadBalancer` - * `getLoadBalancers` - -## Machine Learning - -* `Prediction.details` - * Old type: `Map` - * New type: `Map` - * Operation: `predict` - -## Marketplace Entitlement Service - -* `GetEntitlementsRequest.filter` - * Old type: `Map>` - * New type: `Map>` - * Operation: `getEntitlements` - -## Omics - -* `TsvStoreOptions.formatToHeader` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createAnnotationStore` - * `createAnnotationStore` - * `getAnnotationStore` - * `updateAnnotationStore` -* `TsvVersionOptions.formatToHeader` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createAnnotationStoreVersion` - * `createAnnotationStoreVersion` - * `getAnnotationStoreVersion` - -## OpenSearch - -* `CreateDomainRequest.logPublishingOptions` - * Old type: `Map` - * New type: `Map` - * Operation: `createDomain` -* `DomainStatus.logPublishingOptions` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createDomain` - * `deleteDomain` - * `describeDomain` - * `describeDryRunProgress` - * `describeDomains` -* `LogPublishingOptionsStatus.options` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeDomainConfig` - * `updateDomainConfig` -* `UpdateDomainConfigRequest.logPublishingOptions` - * Old type: `Map` - * New type: `Map` - * Operation: `updateDomainConfig` - -## OpsWorks - -* `App.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `describeApps` -* `CloneStackRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `cloneStack` -* `CreateAppRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `createApp` -* `CreateLayerRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `createLayer` -* `CreateStackRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `createStack` -* `Layer.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `describeLayers` -* `Stack.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `describeStacks` -* `UpdateAppRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `updateApp` -* `UpdateLayerRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `updateLayer` -* `UpdateStackRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `updateStack` - -## Outposts - -* `OrderSummary.lineItemCountsByStatus` - * Old type: `Map` - * New type: `Map` - * Operation: `listOrders` - -## Pinpoint - -* `OpenHours.custom` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` -* `OpenHours.email` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` -* `OpenHours.push` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` -* `OpenHours.sms` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` -* `OpenHours.voice` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - * `createJourney` - * `deleteJourney` - * `getJourney` - * `listJourneys` - * `updateJourney` - * `updateJourneyState` - * `createJourney` - * `updateJourney` - -## Pinpoint SMS Voice V2 - -* `SendDestinationNumberVerificationCodeRequest.destinationCountryParameters` - * Old type: `Map` - * New type: `Map` - * Operation: `sendDestinationNumberVerificationCode` -* `SendTextMessageRequest.destinationCountryParameters` - * Old type: `Map` - * New type: `Map` - * Operation: `sendTextMessage` - -## PrivateNetworks - -* `ListDeviceIdentifiersRequest.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `listDeviceIdentifiers` -* `ListNetworkResourcesRequest.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `listNetworkResources` -* `ListNetworkSitesRequest.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `listNetworkSites` -* `ListNetworksRequest.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `listNetworks` -* `ListOrdersRequest.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `listOrders` - -## Resiliencehub - -* `AppAssessment.compliance` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeAppAssessment` - * `startAppAssessment` -* `AppComponentCompliance.compliance` - * Old type: `Map` - * New type: `Map` - * Operation: `listAppComponentCompliances` -* `ComplianceDrift.actualValue` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listAppAssessmentComplianceDrifts` - * `listAppAssessmentComplianceDrifts` -* `ComplianceDrift.expectedValue` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listAppAssessmentComplianceDrifts` - * `listAppAssessmentComplianceDrifts` -* `ConfigRecommendation.compliance` - * Old type: `Map` - * New type: `Map` - * Operation: `listAppComponentRecommendations` -* `ConfigRecommendation.recommendationCompliance` - * Old type: `Map` - * New type: `Map` - * Operation: `listAppComponentRecommendations` -* `CreateResiliencyPolicyRequest.policy` - * Old type: `Map` - * New type: `Map` - * Operation: `createResiliencyPolicy` -* `ResiliencyPolicy.policy` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeAppAssessment` - * `startAppAssessment` - * `createResiliencyPolicy` - * `describeResiliencyPolicy` - * `listResiliencyPolicies` - * `listSuggestedResiliencyPolicies` - * `updateResiliencyPolicy` -* `ResiliencyScore.componentScore` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeAppAssessment` - * `startAppAssessment` - * `listAppComponentCompliances` -* `ResiliencyScore.disruptionScore` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeAppAssessment` - * `startAppAssessment` - * `listAppComponentCompliances` -* `UpdateResiliencyPolicyRequest.policy` - * Old type: `Map` - * New type: `Map` - * Operation: `updateResiliencyPolicy` - -## SESv2 - -* `BatchGetMetricDataQuery.dimensions` - * Old type: `Map` - * New type: `Map` - * Operation: `batchGetMetricData` -* `ListRecommendationsRequest.filter` - * Old type: `Map` - * New type: `Map` - * Operation: `listRecommendations` -* `MetricsDataSource.dimensions` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createExportJob` - * `getExportJob` - -## SQS - -* `CreateQueueRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `createQueue` -* `GetQueueAttributesResult.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `getQueueAttributes` -* `Message.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `receiveMessage` -* `SendMessageBatchRequestEntry.messageSystemAttributes` - * Old type: `Map` - * New type: `Map` - * Operation: `sendMessageBatch` -* `SendMessageRequest.messageSystemAttributes` - * Old type: `Map` - * New type: `Map` - * Operation: `sendMessage` -* `SetQueueAttributesRequest.attributes` - * Old type: `Map` - * New type: `Map` - * Operation: `setQueueAttributes` - -## SSM Contacts - -* `RecurrenceSettings.shiftCoverages` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createRotation` - * `getRotation` - * `listPreviewRotationShifts` - * `listRotations` - * `updateRotation` - -## SageMaker - -* `AutoMlCandidate.inferenceContainerDefinitions` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `listCandidatesForAutoMlJob` - * `describeAutoMlJob` - * `describeAutoMlJobV2` -* `TimeSeriesTransformations.filling` - * Old type: `Map>` - * New type: `Map>` - * Operations: - * `createAutoMlJobV2` - * `describeAutoMlJobV2` - -## Service Catalog - -* `CopyProductInput.sourceProvisioningArtifactIdentifiers` - * Old type: `List>` - * New type: `List>` - * Operation: `copyProduct` -* `CreateServiceActionInput.definition` - * Old type: `Map` - * New type: `Map` - * Operation: `createServiceAction` -* `SearchProductsAsAdminInput.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `searchProductsAsAdmin` -* `SearchProductsInput.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `searchProducts` -* `SearchProvisionedProductsInput.filters` - * Old type: `Map>` - * New type: `Map>` - * Operation: `searchProvisionedProducts` -* `ServiceActionDetail.definition` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createServiceAction` - * `describeServiceAction` - * `updateServiceAction` -* `UpdateProvisionedProductPropertiesInput.provisionedProductProperties` - * Old type: `Map` - * New type: `Map` - * Operation: `updateProvisionedProductProperties` -* `UpdateProvisionedProductPropertiesOutput.provisionedProductProperties` - * Old type: `Map` - * New type: `Map` - * Operation: `updateProvisionedProductProperties` -* `UpdateServiceActionInput.definition` - * Old type: `Map` - * New type: `Map` - * Operation: `updateServiceAction` - -## ServiceDiscovery - -* `Operation.targets` - * Old type: `Map` - * New type: `Map` - * Operation: `getOperation` - -## Transcribe - -* `CallAnalyticsJobSettings.languageIdSettings` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getCallAnalyticsJob` - * `startCallAnalyticsJob` - * `startCallAnalyticsJob` -* `StartTranscriptionJobRequest.languageIdSettings` - * Old type: `Map` - * New type: `Map` - * Operation: `startTranscriptionJob` -* `TranscriptionJob.languageIdSettings` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getTranscriptionJob` - * `startTranscriptionJob` - -## WAFV2 - -* `AssociationConfig.requestBody` - * Old type: `Map` - * New type: `Map` - * Operations: - * `createWebAcl` - * `updateWebAcl` - * `getWebAclForResource` - * `getWebAcl` - -## WellArchitected - -* `CheckSummary.accountSummary` - * Old type: `Map` - * New type: `Map` - * Operation: `listCheckSummaries` -* `ConsolidatedReportMetric.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operation: `getConsolidatedReport` -* `LensMetric.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operation: `getConsolidatedReport` -* `LensReview.prioritizedRiskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getLensReview` - * `updateLensReview` - * `getLensReview` - * `updateLensReview` -* `LensReview.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getLensReview` - * `updateLensReview` - * `getLensReview` - * `updateLensReview` -* `LensReviewSummary.prioritizedRiskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listLensReviews` - * `listLensReviews` -* `LensReviewSummary.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listLensReviews` - * `listLensReviews` -* `PillarMetric.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operation: `getConsolidatedReport` -* `PillarReviewSummary.prioritizedRiskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getLensReview` - * `updateLensReview` - * `getLensReview` - * `updateLensReview` -* `PillarReviewSummary.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getLensReview` - * `updateLensReview` - * `getLensReview` - * `updateLensReview` -* `ReviewTemplate.questionCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getReviewTemplate` - * `updateReviewTemplate` -* `ReviewTemplateLensReview.questionCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getReviewTemplateLensReview` - * `updateReviewTemplateLensReview` -* `ReviewTemplatePillarReviewSummary.questionCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getReviewTemplateLensReview` - * `updateReviewTemplateLensReview` -* `Workload.prioritizedRiskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getWorkload` - * `getMilestone` - * `updateWorkload` - * `getWorkload` - * `getMilestone` - * `updateWorkload` -* `Workload.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `getWorkload` - * `getMilestone` - * `updateWorkload` - * `getWorkload` - * `getMilestone` - * `updateWorkload` -* `WorkloadSummary.prioritizedRiskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listMilestones` - * `listWorkloads` - * `listMilestones` - * `listWorkloads` -* `WorkloadSummary.riskCounts` - * Old type: `Map` - * New type: `Map` - * Operations: - * `listMilestones` - * `listWorkloads` - * `listMilestones` - * `listWorkloads` - -## WorkDocs - -* `DocumentVersionMetadata.source` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeFolderContents` - * `getResources` - * `getDocument` - * `initiateDocumentVersionUpload` - * `searchResources` - * `describeDocumentVersions` - * `getDocumentVersion` - * `searchResources` -* `DocumentVersionMetadata.thumbnail` - * Old type: `Map` - * New type: `Map` - * Operations: - * `describeFolderContents` - * `getResources` - * `getDocument` - * `initiateDocumentVersionUpload` - * `searchResources` - * `describeDocumentVersions` - * `getDocumentVersion` - * `searchResources` - -# Feedback - -If you have any questions concerning this change, please feel free to engage with us in this discussion. If you encounter a bug with these changes, please [file an issue](https://github.com/awslabs/aws-sdk-kotlin/issues/new/choose).