diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ab6d3a4d52..59d52f360b3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ smithy-kotlin-codegen-version = "0.35.22" smithy-version = "1.64.0" # testing -ddb-local-version = "2.5.2" +ddb-local-version = "2.6.1" junit-version = "5.13.2" kotest-version = "5.9.1" kotlinx-benchmark-version = "0.4.12" diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt index 64ed31b5d31..5e0f9f92f82 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt @@ -48,16 +48,55 @@ public object MapperTypes { public fun itemSchema(typeVar: String): TypeRef = TypeRef(MapperPkg.Hl.Items, "ItemSchema", genericArgs = listOf(TypeVar(typeVar))) - public fun itemSchemaPartitionKey(objectType: TypeRef, pkType: TypeRef): TypeRef = - TypeRef(MapperPkg.Hl.Items, "ItemSchema.PartitionKey", genericArgs = listOf(objectType, pkType)) + public fun itemSchemaPartitionKey(objectType: TypeRef, pkTypes: List): TypeRef = + TypeRef( + MapperPkg.Hl.Items, + "ItemSchema.PartitionKey", + genericArgs = listOf( + objectType, + keyType(pkTypes), + ), + ) + + public fun itemSchemaCompositeKey(objectType: TypeRef, pkTypes: List, skTypes: List): TypeRef = + TypeRef( + MapperPkg.Hl.Items, + "ItemSchema.CompositeKey", + genericArgs = listOf( + objectType, + keyType(pkTypes), + keyType(skTypes), + ), + ) + + public fun keySpec(keyTypes: List): TypeRef { + val keySize = keyTypes.size + require(keySize in 1..4) { "KeySpec subtypes must have between 1 and 4 keys, found $keySize" } + return TypeRef(MapperPkg.Hl.Items, "KeySpec.Key$keySize", keyTypes) + } + + public val KeySpecByteArray: TypeRef = TypeRef(MapperPkg.Hl.Items, "KeySpec.byteArray") + public fun keySpecNumber(numberTypeRef: TypeRef? = null): TypeRef = TypeRef( + MapperPkg.Hl.Items, + "KeySpec.number", + genericArgs = listOfNotNull(numberTypeRef), + ) + public val KeySpecString: TypeRef = TypeRef(MapperPkg.Hl.Items, "KeySpec.string") - public fun itemSchemaCompositeKey(objectType: TypeRef, pkType: TypeRef, skType: TypeRef): TypeRef = - TypeRef(MapperPkg.Hl.Items, "ItemSchema.CompositeKey", genericArgs = listOf(objectType, pkType, skType)) + public val KeySpecThenByteArray: TypeRef = TypeRef(MapperPkg.Hl.Items, "thenByteArray") + public fun keySpecThenNumber(numberTypeRef: TypeRef): TypeRef = TypeRef( + MapperPkg.Hl.Items, + "thenNumber", + genericArgs = listOfNotNull(numberTypeRef), + ) + public val KeySpecThenString: TypeRef = TypeRef(MapperPkg.Hl.Items, "thenString") + + public fun keyType(keyTypes: List): TypeRef { + val keySize = keyTypes.size + require(keySize in 1..4) { "KeyType subtypes must have between 1 and 4 keys, found $keySize" } + return TypeRef(MapperPkg.Hl.Items, "KeyType.Key$keySize", keyTypes) + } - public fun keySpec(keyType: TypeRef): TypeRef = TypeRef(MapperPkg.Hl.Items, "KeySpec", genericArgs = listOf(keyType)) - public val KeySpecByteArray: TypeRef = TypeRef(MapperPkg.Hl.Items, "KeySpec.ByteArray") - public val KeySpecNumber: TypeRef = TypeRef(MapperPkg.Hl.Items, "KeySpec.Number") - public val KeySpecString: TypeRef = TypeRef(MapperPkg.Hl.Items, "KeySpec.String") public val AttributeDescriptor: TypeRef = TypeRef(MapperPkg.Hl.Items, "AttributeDescriptor") public fun itemConverter(objectType: TypeRef): TypeRef = @@ -68,16 +107,28 @@ public object MapperTypes { public object Model { public val intersectKeys: TypeRef = TypeRef(MapperPkg.Hl.Model, "intersectKeys") - public fun tablePartitionKey(objectType: TypeRef, pkType: TypeRef): TypeRef = TypeRef( + + public fun tablePartitionKey(objectType: TypeRef, pkTypes: List): TypeRef = TypeRef( MapperPkg.Hl.Model, "Table.PartitionKey", - genericArgs = listOf(objectType, pkType), - ) - public fun tableCompositeKey(objectType: TypeRef, pkType: TypeRef, skType: TypeRef): TypeRef = TypeRef( - MapperPkg.Hl.Model, - "Table.CompositeKey", - genericArgs = listOf(objectType, pkType, skType), + genericArgs = listOf(objectType, Items.keyType(pkTypes)), ) + + public fun tableCompositeKey( + objectType: TypeRef, + pkTypes: List, + skTypes: List, + ): TypeRef { + require(pkTypes.size in 1..4) { "Partition keys must have between 1 and 4 attributes, found ${pkTypes.size}" } + require(skTypes.size in 1..4) { "Sort keys must have between 1 and 4 attributes, found ${skTypes.size}" } + + return TypeRef( + MapperPkg.Hl.Model, + "Table.CompositeKey", + genericArgs = listOf(objectType, Items.keyType(pkTypes), Items.keyType(skTypes)), + ) + } + public val toItem: TypeRef = TypeRef(MapperPkg.Hl.Model, "toItem") } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index fe149516dff..a67e1a5244b 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -51,25 +51,22 @@ internal class SchemaRenderer( private val properties = classDeclaration .getAllProperties() .filterNot { it.modifiers.contains(Modifier.PRIVATE) || it.isAnnotationPresent(DynamoDbIgnore::class) } + .toList() - init { - check(properties.count { it.isPk } == 1) { - "Expected exactly one @DynamoDbPartitionKey annotation on a property" - } - check(properties.count { it.isSk } <= 1) { - "Expected at most one @DynamoDbSortKey annotation on a property" + private val partitionKeyProps = properties.filter { it.isPk }.also { + require(it.size in 1..4) { + "Expected between 1 and 4 properties annotated with @DynamoDbPartitionKey, found ${it.size}" } } - private val partitionKeyProp = properties.single { it.isPk } - private val partitionKeyName = partitionKeyProp - .getAnnotationsByType(DynamoDbAttribute::class) - .singleOrNull()?.name ?: partitionKeyProp.name + private val sortKeyProps = properties.filter { it.isSk }.also { + require(it.size in 0..4) { + "Expected between 0 and 4 properties annotated with @DynamoDbSortKey, found ${it.size}" + } + } - private val sortKeyProp = properties.singleOrNull { it.isSk } - private val sortKeyName = sortKeyProp - ?.getAnnotationsByType(DynamoDbAttribute::class) - ?.singleOrNull()?.name ?: sortKeyProp?.name + private val partitionKeyTypeRefs = partitionKeyProps.map { it.typeRef } + private val sortKeyTypeRefs = sortKeyProps.map { it.typeRef } /** * Skip rendering a class builder if: @@ -283,52 +280,84 @@ internal class SchemaRenderer( } private fun renderSchema() { - val schemaType = if (sortKeyProp != null) { - MapperTypes.Items.itemSchemaCompositeKey(classType, partitionKeyProp.typeRef, sortKeyProp.typeRef) + val schemaType = if (sortKeyProps.isEmpty()) { + MapperTypes.Items.itemSchemaPartitionKey(classType, partitionKeyTypeRefs) } else { - MapperTypes.Items.itemSchemaPartitionKey(classType, partitionKeyProp.typeRef) + MapperTypes.Items.itemSchemaCompositeKey(classType, partitionKeyTypeRefs, sortKeyTypeRefs) } withBlock("#Lobject #L : #T {", "}", ctx.attributes.visibility, schemaName, schemaType) { write("override val converter: #1T = #1T", itemConverter) - write("override val partitionKey: #T = #T(#S)", MapperTypes.Items.keySpec(partitionKeyProp.keySpec), partitionKeyProp.keySpecType, partitionKeyName) - if (sortKeyProp != null) { - write("override val sortKey: #T = #T(#S)", MapperTypes.Items.keySpec(sortKeyProp.keySpec), sortKeyProp.keySpecType, sortKeyName!!) + + writeInline("override val partitionKey: #T = ", MapperTypes.Items.keySpec(partitionKeyTypeRefs)) + keySpecInstantiation(partitionKeyProps) + write() + + if (sortKeyProps.isNotEmpty()) { + writeInline("override val sortKey: #T = ", MapperTypes.Items.keySpec(sortKeyTypeRefs)) + keySpecInstantiation(sortKeyProps) + write() } } + blankLine() } - private val KSPropertyDeclaration.keySpec: TypeRef - get() = when (typeName) { - "kotlin.ByteArray" -> Types.Kotlin.ByteArray - "kotlin.Int" -> Types.Kotlin.Number - "kotlin.String" -> Types.Kotlin.String - else -> error("Unsupported key type $typeName, expected ByteArray, Int, or String") + private fun keySpecInstantiation(keyProps: List) { + val first = keyProps.first() + val rest = keyProps.drop(1) + + val firstTypeRef = when (first.typeRef) { + Types.Kotlin.ByteArray -> MapperTypes.Items.KeySpecByteArray + + Types.Kotlin.Byte, + Types.Kotlin.Int, + Types.Kotlin.Long, + Types.Kotlin.Short, + -> MapperTypes.Items.keySpecNumber(first.typeRef) + + Types.Kotlin.String -> MapperTypes.Items.KeySpecString + + else -> error("Unsupported key attribute type ${first.typeRef}") } - private val KSPropertyDeclaration.keySpecType: TypeRef - get() = when (typeName) { - "kotlin.ByteArray" -> MapperTypes.Items.KeySpecByteArray - "kotlin.Int" -> MapperTypes.Items.KeySpecNumber - "kotlin.String" -> MapperTypes.Items.KeySpecString - else -> error("Unsupported key type $typeName, expected ByteArray, Int, or String") + writeInline("#T(#S)", firstTypeRef, first.ddbName) + + rest.forEach { prop -> + val typeRef = when (prop.typeRef) { + Types.Kotlin.ByteArray -> MapperTypes.Items.KeySpecThenByteArray + + Types.Kotlin.Byte, + Types.Kotlin.Int, + Types.Kotlin.Long, + Types.Kotlin.Short, + -> MapperTypes.Items.keySpecThenNumber(prop.typeRef) + + Types.Kotlin.String -> MapperTypes.Items.KeySpecThenString + + else -> error("Unsupported key attribute type ${prop.typeRef}") + } + + writeInline(".#T(#S)", typeRef, prop.ddbName) } + } private fun renderGetTable() { docs("Returns a reference to a table named [name] containing items representing [#T]", classType) + val tableType = if (sortKeyProps.isEmpty()) { + MapperTypes.Model.tablePartitionKey(classType, partitionKeyTypeRefs) + } else { + MapperTypes.Model.tableCompositeKey(classType, partitionKeyTypeRefs, sortKeyTypeRefs) + } + val fnName = "get${className}Table" write( "#Lfun #T.#L(name: String): #T = #L(name, #L)", ctx.attributes.visibility, MapperTypes.DynamoDbMapper, fnName, - if (sortKeyProp != null) { - MapperTypes.Model.tableCompositeKey(classType, partitionKeyProp.typeRef, sortKeyProp.typeRef) - } else { - MapperTypes.Model.tablePartitionKey(classType, partitionKeyProp.typeRef) - }, + tableType, "getTable", schemaName, ) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt index d39cf9ff92f..ef155699d83 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt @@ -38,21 +38,21 @@ class SchemaGeneratorPluginTest { // Apply the plugin and necessary dependencies val buildFileContent = """ - repositories { - mavenCentral() - mavenLocal() - } - - plugins { - id("org.jetbrains.kotlin.jvm") version "$kotlinVersion" - id("aws.sdk.kotlin.hll.dynamodbmapper.schema.generator") - } - - dependencies { - implementation("aws.sdk.kotlin:dynamodb-mapper:$sdkVersion") - implementation("aws.sdk.kotlin:dynamodb-mapper-annotations:$sdkVersion") - implementation("aws.sdk.kotlin:dynamodb-mapper-schema-generator-plugin:$sdkVersion") - } + repositories { + mavenCentral() + mavenLocal() + } + + plugins { + id("org.jetbrains.kotlin.jvm") version "$kotlinVersion" + id("aws.sdk.kotlin.hll.dynamodbmapper.schema.generator") + } + + dependencies { + implementation("aws.sdk.kotlin:dynamodb-mapper:$sdkVersion") + implementation("aws.sdk.kotlin:dynamodb-mapper-annotations:$sdkVersion") + implementation("aws.sdk.kotlin:dynamodb-mapper-schema-generator-plugin:$sdkVersion") + } """.trimIndent() buildFile.writeText(buildFileContent) @@ -102,36 +102,36 @@ class SchemaGeneratorPluginTest { assertContains( schemaContents, """ - object UserConverter : ItemConverter by SimpleItemConverter( - builderFactory = ::UserBuilder, - build = UserBuilder::build, - descriptors = arrayOf( - AttributeDescriptor( - "id", - User::id, - UserBuilder::id::set, - NumberValueConverters.Int, - ), - AttributeDescriptor( - "fName", - User::givenName, - UserBuilder::givenName::set, - StringValueConverter, - ), - AttributeDescriptor( - "lName", - User::surname, - UserBuilder::surname::set, - StringValueConverter, - ), - AttributeDescriptor( - "age", - User::age, - UserBuilder::age::set, - NumberValueConverters.Int, - ), - ), - ) + public object UserConverter : ItemConverter by SimpleItemConverter( + builderFactory = ::UserBuilder, + build = UserBuilder::build, + descriptors = arrayOf( + AttributeDescriptor( + "id", + User::id, + UserBuilder::id::set, + NumberValueConverters.Int, + ), + AttributeDescriptor( + "fName", + User::givenName, + UserBuilder::givenName::set, + StringValueConverter, + ), + AttributeDescriptor( + "lName", + User::surname, + UserBuilder::surname::set, + StringValueConverter, + ), + AttributeDescriptor( + "age", + User::age, + UserBuilder::age::set, + NumberValueConverters.Int, + ), + ), + ) """.trimIndent(), ) @@ -139,15 +139,18 @@ class SchemaGeneratorPluginTest { assertContains( schemaContents, """ - object UserSchema : ItemSchema.PartitionKey { - override val converter: UserConverter = UserConverter - override val partitionKey: KeySpec = KeySpec.Number("id") - } + public object UserSchema : ItemSchema.PartitionKey> { + override val converter: UserConverter = UserConverter + override val partitionKey: KeySpec.Key1 = KeySpec.number("id") + } """.trimIndent(), ) // GetTable - assertContains(schemaContents, "fun DynamoDbMapper.getUserTable(name: String): Table.PartitionKey = getTable(name, UserSchema)".trimIndent()) + assertContains( + schemaContents, + "public fun DynamoDbMapper.getUserTable(name: String): Table.PartitionKey> = getTable(name, UserSchema)", + ) } @Test @@ -169,36 +172,36 @@ class SchemaGeneratorPluginTest { assertContains( schemaContents, """ - object BuilderNotRequiredConverter : ItemConverter by SimpleItemConverter( - builderFactory = { BuilderNotRequired() }, - build = { this }, - descriptors = arrayOf( - AttributeDescriptor( - "id", - BuilderNotRequired::id, - BuilderNotRequired::id::set, - NumberValueConverters.Int, - ), - AttributeDescriptor( - "fName", - BuilderNotRequired::givenName, - BuilderNotRequired::givenName::set, - StringValueConverter, - ), - AttributeDescriptor( - "lName", - BuilderNotRequired::surname, - BuilderNotRequired::surname::set, - StringValueConverter, - ), - AttributeDescriptor( - "age", - BuilderNotRequired::age, - BuilderNotRequired::age::set, - NumberValueConverters.Int, - ), - ), - ) + public object BuilderNotRequiredConverter : ItemConverter by SimpleItemConverter( + builderFactory = { BuilderNotRequired() }, + build = { this }, + descriptors = arrayOf( + AttributeDescriptor( + "id", + BuilderNotRequired::id, + BuilderNotRequired::id::set, + NumberValueConverters.Int, + ), + AttributeDescriptor( + "fName", + BuilderNotRequired::givenName, + BuilderNotRequired::givenName::set, + StringValueConverter, + ), + AttributeDescriptor( + "lName", + BuilderNotRequired::surname, + BuilderNotRequired::surname::set, + StringValueConverter, + ), + AttributeDescriptor( + "age", + BuilderNotRequired::age, + BuilderNotRequired::age::set, + NumberValueConverters.Int, + ), + ), + ) """.trimIndent(), ) } @@ -206,12 +209,12 @@ class SchemaGeneratorPluginTest { @Test fun testGenerateBuilderOption() { val pluginConfiguration = """ - import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.GenerateBuilderClasses - - dynamoDbMapper { - generateBuilderClasses = GenerateBuilderClasses.ALWAYS - } - + import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.GenerateBuilderClasses + + dynamoDbMapper { + generateBuilderClasses = GenerateBuilderClasses.ALWAYS + } + """.trimIndent() buildFile.prependText(pluginConfiguration) @@ -237,12 +240,12 @@ class SchemaGeneratorPluginTest { @Test fun testVisibilityOption() { val pluginConfiguration = """ - import aws.sdk.kotlin.hll.codegen.rendering.Visibility - - dynamoDbMapper { - visibility = Visibility.INTERNAL - } - + import aws.sdk.kotlin.hll.codegen.rendering.Visibility + + dynamoDbMapper { + visibility = Visibility.INTERNAL + } + """.trimIndent() buildFile.prependText(pluginConfiguration) @@ -266,10 +269,10 @@ class SchemaGeneratorPluginTest { @Test fun testGenerateGetTableFunctionOption() { val pluginConfiguration = """ - dynamoDbMapper { - generateGetTableExtension = false - } - + dynamoDbMapper { + generateGetTableExtension = false + } + """.trimIndent() buildFile.prependText(pluginConfiguration) @@ -293,12 +296,12 @@ class SchemaGeneratorPluginTest { @Test fun testRelativeDestinationPackage() { val pluginConfiguration = """ - import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.DestinationPackage - - dynamoDbMapper { - destinationPackage = DestinationPackage.Relative("hello.moto") - } - + import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.DestinationPackage + + dynamoDbMapper { + destinationPackage = DestinationPackage.Relative("hello.moto") + } + """.trimIndent() buildFile.prependText(pluginConfiguration) @@ -318,12 +321,12 @@ class SchemaGeneratorPluginTest { @Test fun testAbsoluteDestinationPackage() { val pluginConfiguration = """ - import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.DestinationPackage - - dynamoDbMapper { - destinationPackage = DestinationPackage.Absolute("absolutely.my.`package`") - } - + import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.DestinationPackage + + dynamoDbMapper { + destinationPackage = DestinationPackage.Absolute("absolutely.my.`package`") + } + """.trimIndent() buildFile.prependText(pluginConfiguration) @@ -344,9 +347,9 @@ class SchemaGeneratorPluginTest { fun testGeneratedItemConverter() { buildFile.appendText( """ - dependencies { - testImplementation(kotlin("test")) - } + dependencies { + testImplementation(kotlin("test")) + } """.trimIndent(), ) @@ -406,10 +409,10 @@ class SchemaGeneratorPluginTest { assertContains( schemaContents, """ - public object CustomUserSchema : ItemSchema.PartitionKey { - override val converter: MyCustomUserConverter = MyCustomUserConverter - override val partitionKey: KeySpec = KeySpec.Number("id") - } + public object CustomUserSchema : ItemSchema.PartitionKey> { + override val converter: MyCustomUserConverter = MyCustomUserConverter + override val partitionKey: KeySpec.Key1 = KeySpec.number("id") + } """.trimIndent(), ) } @@ -418,10 +421,10 @@ class SchemaGeneratorPluginTest { fun testPrimitives() { buildFile.appendText( """ - dependencies { - implementation("aws.smithy.kotlin:runtime-core:$smithyKotlinVersion") - testImplementation(kotlin("test")) - } + dependencies { + implementation("aws.smithy.kotlin:runtime-core:$smithyKotlinVersion") + testImplementation(kotlin("test")) + } """.trimIndent(), ) @@ -445,10 +448,10 @@ class SchemaGeneratorPluginTest { fun testNullableTypes() { buildFile.appendText( """ - dependencies { - implementation("aws.smithy.kotlin:runtime-core:$smithyKotlinVersion") - testImplementation(kotlin("test")) - } + dependencies { + implementation("aws.smithy.kotlin:runtime-core:$smithyKotlinVersion") + testImplementation(kotlin("test")) + } """.trimIndent(), ) @@ -472,9 +475,9 @@ class SchemaGeneratorPluginTest { fun testLists() { buildFile.appendText( """ - dependencies { - testImplementation(kotlin("test")) - } + dependencies { + testImplementation(kotlin("test")) + } """.trimIndent(), ) @@ -498,9 +501,9 @@ class SchemaGeneratorPluginTest { fun testSets() { buildFile.appendText( """ - dependencies { - testImplementation(kotlin("test")) - } + dependencies { + testImplementation(kotlin("test")) + } """.trimIndent(), ) @@ -524,9 +527,9 @@ class SchemaGeneratorPluginTest { fun testMaps() { buildFile.appendText( """ - dependencies { - testImplementation(kotlin("test")) - } + dependencies { + testImplementation(kotlin("test")) + } """.trimIndent(), ) @@ -562,10 +565,10 @@ class SchemaGeneratorPluginTest { assertContains( schemaContents, """ - object RenamedPartitionKeySchema : ItemSchema.PartitionKey { - override val converter: RenamedPartitionKeyConverter = RenamedPartitionKeyConverter - override val partitionKey: KeySpec = KeySpec.Number("user_id") - } + public object RenamedPartitionKeySchema : ItemSchema.PartitionKey> { + override val converter: RenamedPartitionKeyConverter = RenamedPartitionKeyConverter + override val partitionKey: KeySpec.Key1 = KeySpec.number("user_id") + } """.trimIndent(), ) } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api b/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api index 72175f49af3..81221f6f0da 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api +++ b/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api @@ -410,13 +410,15 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/InExprKt { } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter { - public abstract fun getPartitionKey ()Ljava/lang/Object; - public abstract fun getSortKey ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun getPartitionKey ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType; + public abstract fun getSortKeyExpressions ()Ljava/util/List; } public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilterKt { - public static final fun KeyFilter (Ljava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; - public static final fun KeyFilter (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; + public static final fun KeyFilter (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType;[Lkotlin/jvm/functions/Function1;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; + public static final fun KeyFilter (Ljava/lang/Number;[Lkotlin/jvm/functions/Function1;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; + public static final fun KeyFilter (Ljava/lang/String;[Lkotlin/jvm/functions/Function1;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; + public static final fun KeyFilter ([B[Lkotlin/jvm/functions/Function1;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression { @@ -654,25 +656,26 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/items/HeterogeneousItemConv public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema { public abstract fun getConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; - public abstract fun getKeyAttributeNames ()Ljava/util/Set; + public abstract fun getKeyAttributeNames ()Ljava/util/List; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey : aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$PartitionKey { - public fun getKeyAttributeNames ()Ljava/util/Set; +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey : aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema { + public fun getKeyAttributeNames ()Ljava/util/List; + public abstract fun getPartitionKey ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec; public abstract fun getSortKey ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec; } public final class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey$DefaultImpls { - public static fun getKeyAttributeNames (Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey;)Ljava/util/Set; + public static fun getKeyAttributeNames (Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey;)Ljava/util/List; } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$PartitionKey : aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema { - public fun getKeyAttributeNames ()Ljava/util/Set; + public fun getKeyAttributeNames ()Ljava/util/List; public abstract fun getPartitionKey ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec; } public final class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$PartitionKey$DefaultImpls { - public static fun getKeyAttributeNames (Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$PartitionKey;)Ljava/util/Set; + public static fun getKeyAttributeNames (Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$PartitionKey;)Ljava/util/List; } public final class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchemaKt { @@ -682,25 +685,149 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchemaKt { public static final fun withKeySpec (Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec;Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec;)Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { - public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Companion; +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec { + public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec$Companion; public abstract fun getName ()Ljava/lang/String; + public abstract fun getType ()Laws/sdk/kotlin/services/dynamodb/model/ScalarAttributeType; public abstract fun toField (Ljava/lang/Object;)Lkotlin/Pair; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$ByteArray : aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec$Companion { + public final fun byteArray (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public final fun number (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public final fun string (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { + public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Companion; + public abstract fun toFields (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; } public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Companion { - public final fun ByteArray (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$ByteArray; - public final fun Number (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Number; - public final fun String (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$String; + public final fun byteArray (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key1; + public final fun number (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key1; + public final fun string (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key1; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key1 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { + public abstract fun getAttr1 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key2 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { + public abstract fun getAttr1 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public abstract fun getAttr2 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key3 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { + public abstract fun getAttr1 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public abstract fun getAttr2 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public abstract fun getAttr3 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key4 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { + public abstract fun getAttr1 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public abstract fun getAttr2 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public abstract fun getAttr3 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; + public abstract fun getAttr4 ()Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyAttrSpec; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey1ExtKt { + public static final fun thenByteArray (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key1;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key2; + public static final fun thenNumber (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key1;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key2; + public static final fun thenString (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key1;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key2; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey2ExtKt { + public static final fun thenByteArray (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key2;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key3; + public static final fun thenNumber (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key2;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key3; + public static final fun thenString (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key2;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key3; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey3ExtKt { + public static final fun thenByteArray (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key3;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key4; + public static final fun thenNumber (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key3;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key4; + public static final fun thenString (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key3;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Key4; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType { } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$Number : aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType { + public fun component1 ()Ljava/lang/Object; + public abstract fun getValue1 ()Ljava/lang/Object; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$String : aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec { +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1$DefaultImpls { + public static fun component1 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1;)Ljava/lang/Object; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType { + public fun component1 ()Ljava/lang/Object; + public fun component2 ()Ljava/lang/Object; + public abstract fun getValue1 ()Ljava/lang/Object; + public abstract fun getValue2 ()Ljava/lang/Object; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2$DefaultImpls { + public static fun component1 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2;)Ljava/lang/Object; + public static fun component2 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2;)Ljava/lang/Object; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType { + public fun component1 ()Ljava/lang/Object; + public fun component2 ()Ljava/lang/Object; + public fun component3 ()Ljava/lang/Object; + public abstract fun getValue1 ()Ljava/lang/Object; + public abstract fun getValue2 ()Ljava/lang/Object; + public abstract fun getValue3 ()Ljava/lang/Object; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3$DefaultImpls { + public static fun component1 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3;)Ljava/lang/Object; + public static fun component2 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3;)Ljava/lang/Object; + public static fun component3 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3;)Ljava/lang/Object; +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4 : aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType { + public fun component1 ()Ljava/lang/Object; + public fun component2 ()Ljava/lang/Object; + public fun component3 ()Ljava/lang/Object; + public fun component4 ()Ljava/lang/Object; + public abstract fun getValue1 ()Ljava/lang/Object; + public abstract fun getValue2 ()Ljava/lang/Object; + public abstract fun getValue3 ()Ljava/lang/Object; + public abstract fun getValue4 ()Ljava/lang/Object; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4$DefaultImpls { + public static fun component1 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4;)Ljava/lang/Object; + public static fun component2 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4;)Ljava/lang/Object; + public static fun component3 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4;)Ljava/lang/Object; + public static fun component4 (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4;)Ljava/lang/Object; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey1ExtKt { + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2; + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2; + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1;[B)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey2ExtKt { + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3; + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3; + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key2;[B)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey3ExtKt { + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4; + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4; + public static final fun invoke (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key3;[B)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key4; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKt { + public static final fun Key (Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1; + public static final fun Key (Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1; + public static final fun Key ([B)Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType$Key1; } public final class aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverter : aws/sdk/kotlin/hll/mapping/core/converters/Converter { @@ -728,11 +855,11 @@ public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpe public abstract fun getTableName ()Ljava/lang/String; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec$CompositeKey : aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec$CompositeKey : aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec, aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec$CompositeKey { public abstract fun getSchema ()Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec$PartitionKey : aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec$PartitionKey : aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec, aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec$PartitionKey { public abstract fun getSchema ()Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$PartitionKey; } @@ -783,22 +910,37 @@ public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/Table : } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey : aws/sdk/kotlin/hll/dynamodbmapper/model/ItemSource$CompositeKey, aws/sdk/kotlin/hll/dynamodbmapper/model/Table { - public abstract fun getItem (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType;Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/Table$PartitionKey : aws/sdk/kotlin/hll/dynamodbmapper/model/ItemSource$PartitionKey, aws/sdk/kotlin/hll/dynamodbmapper/model/Table { - public abstract fun getItem (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/items/KeyType;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/model/TableGetItemExtKt { + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;Ljava/lang/Number;Ljava/lang/Number;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;Ljava/lang/Number;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;Ljava/lang/Number;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;Ljava/lang/String;Ljava/lang/Number;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;Ljava/lang/String;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;[BLjava/lang/Number;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;[BLjava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$CompositeKey;[B[BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$PartitionKey;Ljava/lang/Number;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$PartitionKey;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Table$PartitionKey;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec : aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec { public abstract fun getTableName ()Ljava/lang/String; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec$CompositeKey : aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec$CompositeKey : aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec$CompositeKey, aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec { public abstract fun getSchema ()Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$CompositeKey; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec$PartitionKey : aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec$PartitionKey : aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec$PartitionKey, aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec { public abstract fun getSchema ()Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema$PartitionKey; } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapper.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapper.kt index 74f0c8b0031..259a8d89f00 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapper.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapper.kt @@ -9,6 +9,7 @@ import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper.Config.Companion.Builder import aws.sdk.kotlin.hll.dynamodbmapper.internal.DynamoDbMapperImpl import aws.sdk.kotlin.hll.dynamodbmapper.internal.MapperConfigBuilderImpl import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.model.Table import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.Interceptor import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.InterceptorAny @@ -40,11 +41,11 @@ public interface DynamoDbMapper { /** * Get a [Table] reference for performing table operations * @param T The type of objects which will be read from and/or written to this table - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations * @param name The name of the table * @param schema The [ItemSchema] which describes the table, its keys, and how items are converted */ - public fun getTable( + public fun getTable( name: String, schema: ItemSchema.PartitionKey, ): Table.PartitionKey @@ -52,12 +53,12 @@ public interface DynamoDbMapper { /** * Get a [Table] reference for performing table operations * @param T The type of objects which will be read from and/or written to this table - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations * @param name The name of the table * @param schema The [ItemSchema] which describes the table, its keys, and how items are converted */ - public fun getTable( + public fun getTable( name: String, schema: ItemSchema.CompositeKey, ): Table.CompositeKey diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter.kt index ad10c72d76c..e0c74539dd1 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter.kt @@ -6,6 +6,8 @@ package aws.sdk.kotlin.hll.dynamodbmapper.expressions import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.KeyFilterImpl import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.SortKeyFilterImpl +import aws.sdk.kotlin.hll.dynamodbmapper.items.Key +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType /** * Represents a filter which limits a Query operation to a specific partition key and optional sort key criteria (if @@ -15,27 +17,39 @@ public interface KeyFilter { /** * The required value of the partition key */ - public val partitionKey: Any + public val partitionKey: KeyType /** - * The sort key expression (if set) + * The sort key expressions, in key order */ - public val sortKey: SortKeyExpr? + public val sortKeyExpressions: List } /** - * Creates a new [KeyFilter] for a partition key - * @param partitionKey The value required for the partition key. This must be set to a byte array, string, or number - * (including unsigned numbers). + * Creates a new [KeyFilter] implementation for the given partition key value and optional sort key expressions + * @param partitionKey The partition key value to assert + * @param sortKeyExpressions Up to 4 sort key expressions in sort key attribute order */ -public fun KeyFilter(partitionKey: Any): KeyFilter = KeyFilterImpl(partitionKey, null) +public fun KeyFilter(partitionKey: ByteArray, vararg sortKeyExpressions: SortKeyFilter.() -> SortKeyExpr): KeyFilter = KeyFilter(Key(partitionKey), *sortKeyExpressions) /** - * Creates a new [KeyFilter] for a partition key and sort key. Note that using this overload requires a schema with a - * composite key. - * @param partitionKey The value required for the partition key. This must be set to a byte array, string, or number - * (including unsigned numbers). - * @param sortKey A DSL block that sets the condition for the sort key. See [SortKeyFilter] for more details. + * Creates a new [KeyFilter] implementation for the given partition key value and optional sort key expressions + * @param partitionKey The partition key value to assert + * @param sortKeyExpressions Up to 4 sort key expressions in sort key attribute order */ -public fun KeyFilter(partitionKey: Any, sortKey: SortKeyFilter.() -> SortKeyExpr): KeyFilter = - KeyFilterImpl(partitionKey, SortKeyFilterImpl.run(sortKey)) +public fun KeyFilter(partitionKey: Number, vararg sortKeyExpressions: SortKeyFilter.() -> SortKeyExpr): KeyFilter = KeyFilter(Key(partitionKey), *sortKeyExpressions) + +/** + * Creates a new [KeyFilter] implementation for the given partition key value and optional sort key expressions + * @param partitionKey The partition key value to assert + * @param sortKeyExpressions Up to 4 sort key expressions in sort key attribute order + */ +public fun KeyFilter(partitionKey: String, vararg sortKeyExpressions: SortKeyFilter.() -> SortKeyExpr): KeyFilter = KeyFilter(Key(partitionKey), *sortKeyExpressions) + +/** + * Creates a new [KeyFilter] implementation for the given partition key value and optional sort key expressions + * @param partitionKey The partition key value to assert + * @param sortKeyExpressions Up to 4 sort key expressions in sort key attribute order + */ +public fun KeyFilter(partitionKey: KeyType, vararg sortKeyExpressions: SortKeyFilter.() -> SortKeyExpr): KeyFilter = + KeyFilterImpl(partitionKey, SortKeyFilterImpl.run { sortKeyExpressions.map { it() } }) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/FilterImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/FilterImpl.kt index f1ad451759a..308a5c93d6b 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/FilterImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/FilterImpl.kt @@ -8,13 +8,11 @@ import aws.sdk.kotlin.hll.dynamodbmapper.expressions.* internal data object FilterImpl : Filter { // ATTRIBUTES - override fun attr(name: String) = AttributePath(name) override fun AttributePath.get(index: Int) = AttributePath(index, parent = this) override fun AttributePath.get(key: String) = AttributePath(key, parent = this) // BINARY OPERATORS - override fun Expression.eq(expr: Expression) = ComparisonExpr(Comparator.EQUALS, this, expr) override fun Expression.neq(expr: Expression) = ComparisonExpr(Comparator.NOT_EQUALS, this, expr) override fun Expression.lt(expr: Expression) = ComparisonExpr(Comparator.LESS_THAN, this, expr) @@ -23,12 +21,10 @@ internal data object FilterImpl : Filter { override fun Expression.gte(expr: Expression) = ComparisonExpr(Comparator.GREATER_THAN_OR_EQUAL, this, expr) // RANGES & SETS - override fun AttributePath.isBetween(min: Expression, max: Expression) = BetweenExpr(this, min, max) override fun AttributePath.isIn(set: Collection) = InExpr(this, set) // FUNCTIONS - override fun AttributePath.contains(expr: Expression) = BooleanFuncExpr(BooleanFunc.CONTAINS, this, expr) override fun AttributePath.exists() = BooleanFuncExpr(BooleanFunc.ATTRIBUTE_EXISTS, this) override fun AttributePath.notExists() = BooleanFuncExpr(BooleanFunc.ATTRIBUTE_NOT_EXISTS, this) @@ -37,7 +33,6 @@ internal data object FilterImpl : Filter { override fun AttributePath.startsWith(expr: Expression) = BooleanFuncExpr(BooleanFunc.BEGINS_WITH, this, expr) // BOOLEAN LOGIC - override fun and(conditions: List) = AndExpr(conditions) override fun not(condition: BooleanExpr) = NotExpr(condition) override fun or(conditions: List) = OrExpr(conditions) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/KeyFilterImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/KeyFilterImpl.kt index 29fda70de05..3593eaa60e8 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/KeyFilterImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/KeyFilterImpl.kt @@ -6,48 +6,57 @@ package aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal import aws.sdk.kotlin.hll.dynamodbmapper.expressions.* import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.attrs +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.values import aws.sdk.kotlin.hll.dynamodbmapper.util.dynamicAttr -import aws.sdk.kotlin.hll.dynamodbmapper.util.requireNull - -internal data class KeyFilterImpl(override val partitionKey: Any, override val sortKey: SortKeyExpr?) : KeyFilter { - init { - require( - partitionKey is ByteArray || - partitionKey is Number || - partitionKey is String || - partitionKey is UByte || - partitionKey is UInt || - partitionKey is ULong || - partitionKey is UShort, - ) { "Partition key values must be either a ByteArray, Number, String, or an unsigned number type" } - } -} -internal fun KeyFilter.toExpression(schema: ItemSchema<*>) = when (schema) { - is ItemSchema.CompositeKey<*, *, *> -> { - val pkCondition = pkCondition(schema, partitionKey) +internal data class KeyFilterImpl( + override val partitionKey: KeyType, + override val sortKeyExpressions: List, +) : KeyFilter - sortKey?.let { sortKey -> - FilterImpl.run { - val skAttr = attr(schema.sortKey.name) - val skCondition = when (sortKey) { - is BetweenExpr -> BetweenExpr(skAttr, sortKey.min, sortKey.max) - is ComparisonExpr -> ComparisonExpr(sortKey.comparator, skAttr, sortKey.right) - is BooleanFuncExpr -> BooleanFuncExpr(sortKey.func, skAttr, sortKey.additionalOperands) - } +internal fun KeyFilter.toExpression(schema: ItemSchema<*>): Expression { + val conditions = when (schema) { + is ItemSchema.PartitionKey<*, *> -> { + require(sortKeyExpressions.isEmpty()) { "A sort key condition is not allowed on schema without a sort key" } + pkConditions(schema.partitionKey, partitionKey) + } - and(pkCondition, skCondition) - } - } ?: pkCondition + is ItemSchema.CompositeKey<*, *, *> -> + pkConditions(schema.partitionKey, partitionKey) + skConditions(schema.sortKey, sortKeyExpressions) } - is ItemSchema.PartitionKey<*, *> -> { - requireNull(sortKey) { "Sort key condition not allowed on schema without a sort key" } - pkCondition(schema, partitionKey) + return if (conditions.size == 1) conditions.single() else FilterImpl.and(conditions) +} + +private fun pkConditions(spec: KeySpec<*>, value: KeyType): List = FilterImpl.run { + val attrs = spec.attrs + val values = value.values + require(attrs.size == values.size) { + "Provided number of partition keys (${values.size}) does not match the number of keys defined in the schema (${attrs.size})" } - else -> error("Unknown schema type ${schema::class} (expected ItemSchema.CompositeKey or ItemSchema.PartitionKey)") + attrs.zip(values).map { (attr, value) -> + attr(attr.name) eq LiteralExpr(dynamicAttr(value)) + } } -private fun pkCondition(schema: ItemSchema.PartitionKey<*, *>, partitionKey: Any) = - FilterImpl.run { attr(schema.partitionKey.name) eq LiteralExpr(dynamicAttr(partitionKey)) } +private fun skConditions(spec: KeySpec<*>, sortKeyExpressions: List): List = FilterImpl.run { + val attrs = spec.attrs + require(attrs.size >= sortKeyExpressions.size) { + "Provided number of sort key expressions (${sortKeyExpressions.size}) is greater than the number of keys defined in the schema (${attrs.size})" + } + + attrs + .zip(sortKeyExpressions) + .map { (attr, expression) -> + val skAttr = attr(attr.name) + when (expression) { + is BetweenExpr -> BetweenExpr(skAttr, expression.min, expression.max) + is ComparisonExpr -> ComparisonExpr(expression.comparator, skAttr, expression.right) + is BooleanFuncExpr -> BooleanFuncExpr(expression.func, skAttr, expression.additionalOperands) + } + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/internal/DynamoDbMapperImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/internal/DynamoDbMapperImpl.kt index 1206c3d51f7..82fc37127ba 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/internal/DynamoDbMapperImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/internal/DynamoDbMapperImpl.kt @@ -6,6 +6,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.internal import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.model.internal.tableImpl import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.InterceptorAny import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric @@ -20,10 +21,10 @@ internal data class DynamoDbMapperImpl( override val client: DynamoDbClient, override val config: DynamoDbMapper.Config, ) : DynamoDbMapper { - override fun getTable(name: String, schema: ItemSchema.PartitionKey) = + override fun getTable(name: String, schema: ItemSchema.PartitionKey) = tableImpl(this, name, schema) - override fun getTable(name: String, schema: ItemSchema.CompositeKey) = + override fun getTable(name: String, schema: ItemSchema.CompositeKey) = tableImpl(this, name, schema) } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema.kt index 3df886bdf77..712e2d2144c 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema.kt @@ -6,77 +6,83 @@ package aws.sdk.kotlin.hll.dynamodbmapper.items import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.ItemSchemaCompositeKeyImpl import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.ItemSchemaPartitionKeyImpl +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.attrs /** - * Defines a schema for handling objects of a certain type, including an [ItemConverter] for converting between objects + * Defines a schema for handling objects of type [T], including an [ItemConverter] for converting between objects and * items and a [KeySpec] for identifying primary keys. * @param T The type of objects described by this schema */ -public interface ItemSchema { +public sealed interface ItemSchema { /** * The [ItemConverter] used to convert between objects and items */ public val converter: ItemConverter /** - * The name(s) of the attributes which form the primary key of this table + * The names of the attributes which form the primary key of this table */ - public val keyAttributeNames: Set + public val keyAttributeNames: List /** * Represents a schema with a primary key consisting of a single partition key * @param T The type of objects described by this schema - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations */ - public interface PartitionKey : ItemSchema { + public interface PartitionKey : ItemSchema { /** * The [KeySpec] for the partition key */ public val partitionKey: KeySpec - override val keyAttributeNames: Set - get() = setOf(partitionKey.name) + override val keyAttributeNames: List + get() = partitionKey.attrs.map { it.name } } /** * Represents a schema with a primary key that is a composite of a partition key and a sort key * @param T The type of objects described by this schema - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations */ - public interface CompositeKey : PartitionKey { + public interface CompositeKey : ItemSchema { + /** + * The [KeySpec] for the partition key + */ + public val partitionKey: KeySpec + /** * The [KeySpec] for the sort key */ public val sortKey: KeySpec - override val keyAttributeNames: Set - get() = setOf(partitionKey.name, sortKey.name) + override val keyAttributeNames: List + get() = partitionKey.attrs.map { it.name } + sortKey.attrs.map { it.name } } } /** * Create a new item schema with a primary key consisting of a single partition key. * @param T The type of objects described by this schema - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations * @param converter The [ItemConverter] used to convert between objects and items * @param partitionKey The [KeySpec] for the partition key */ @Suppress("FunctionName") -public fun ItemSchema(converter: ItemConverter, partitionKey: KeySpec): ItemSchema.PartitionKey = +public fun ItemSchema(converter: ItemConverter, partitionKey: KeySpec): ItemSchema.PartitionKey = ItemSchemaPartitionKeyImpl(converter, partitionKey) /** * Create a new item schema with a primary key consisting of a single partition key. * @param T The type of objects described by this schema - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations * @param converter The [ItemConverter] used to convert between objects and items * @param partitionKey The [KeySpec] for the partition key * @param sortKey The [KeySpec] for the sort key */ @Suppress("FunctionName") -public fun ItemSchema( +public fun ItemSchema( converter: ItemConverter, partitionKey: KeySpec, sortKey: KeySpec, @@ -85,21 +91,21 @@ public fun ItemSchema( /** * Associate this [ItemConverter] with a [KeySpec] for a partition key to form a complete [ItemSchema] * @param T The type of objects described by this schema - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations * @param partitionKey The [KeySpec] that describes the partition key */ -public fun ItemConverter.withKeySpec(partitionKey: KeySpec): ItemSchema.PartitionKey = +public fun ItemConverter.withKeySpec(partitionKey: KeySpec): ItemSchema.PartitionKey = ItemSchema(this, partitionKey) /** * Associate this [ItemConverter] with [KeySpec] instances for a composite key to form a complete [ItemSchema] * @param T The type of objects described by this schema - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations * @param partitionKey The [KeySpec] that describes the partition key * @param sortKey The [KeySpec] that describes the sort key */ -public fun ItemConverter.withKeySpec( +public fun ItemConverter.withKeySpec( partitionKey: KeySpec, sortKey: KeySpec, ): ItemSchema.CompositeKey = ItemSchema(this, partitionKey, sortKey) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec.kt index e403a56a51e..7784bf034e9 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec.kt @@ -4,68 +4,201 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.items +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec.Companion.byteArray +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec.Companion.number +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec.Companion.string +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeyAttrSpecImpl +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeySpec1Impl +import aws.sdk.kotlin.hll.dynamodbmapper.model.Item import aws.sdk.kotlin.services.dynamodb.model.AttributeValue +import aws.sdk.kotlin.services.dynamodb.model.ScalarAttributeType /** - * Defines a specification for a single key attribute - * @param K The type of the key property, either [kotlin.String], [kotlin.Number], or [kotlin.ByteArray] + * Defines the specification for a partition or sort key, including the names and types of its attributes. Key + * specifications are an important component of item schemas since they describe how to interact with each key attribute + * and provide vital typing information for features which interact with DynamoDB operations like `Query` and `Scan`. + * + * A [KeySpec] consisting of a single attribute may be created manually by invoking one the companion object methods + * [byteArray], [number], or [string]. A [KeySpec] consisting of more attributes may be created by invoking one of the + * extension methods [thenByteArray], [thenNumber], or [thenString]. Key specifications may consist of up to four + * attributes. + * + * **Important**: The order of attributes within a [KeySpec] is significant and must reflect the order of the attributes + * defined in the table or index key within DynamoDB. + * + * ## Examples + * + * To create a key specification for a single attribute: + * + * ```kotlin + * val spec = KeySpec.number("companyId") // returns KeySpec.Key1 + * ``` + * + * To create a key specification for multiple attributes: + * + * ```kotlin + * val spec = KeySpec + * .number("companyId") // returns KeySpec.Key1 + * .thenString("department") // returns KeySpec.Key2 + * .thenNumber<_, Long>("timestamp") // returns KeySpec.Key3 + * ``` + * + * @param K The type of the key, either [KeyType] or one of its specific derivations */ -public sealed interface KeySpec { +public sealed interface KeySpec { /** - * A [KeySpec] for a [kotlin.ByteArray]-typed field + * Given a value for this key, convert into an [Item] map of keys and values + * @param value The value to use for the key attribute + */ + public fun toFields(value: K): Item + + /** + * Defines the specification for a key consisting of a single attribute + * @param K1 The type of the single key attribute, either [String], [Number], or [ByteArray] + */ + public interface Key1 : KeySpec> { + /** + * Gets the specification for the key attribute + */ + public val attr1: KeyAttrSpec + } + + /** + * Defines the specification for a key consisting of two attributes + * @param K1 The type of the first key attribute, either [String], [Number], or [ByteArray] + * @param K2 The type of the second key attribute, either [String], [Number], or [ByteArray] */ - public interface ByteArray : KeySpec + public interface Key2 : KeySpec> { + /** + * Gets the specification for the first key attribute + */ + public val attr1: KeyAttrSpec + + /** + * Gets the specification for the second key attribute + */ + public val attr2: KeyAttrSpec + } /** - * A [KeySpec] for a [kotlin.Number]-typed field + * Defines the specification for a key consisting of three attributes + * @param K1 The type of the first key attribute, either [String], [Number], or [ByteArray] + * @param K2 The type of the second key attribute, either [String], [Number], or [ByteArray] + * @param K3 The type of the third key attribute, either [String], [Number], or [ByteArray] */ - public interface Number : KeySpec + public interface Key3 : KeySpec> { + /** + * Gets the specification for the first key attribute + */ + public val attr1: KeyAttrSpec + + /** + * Gets the specification for the second key attribute + */ + public val attr2: KeyAttrSpec + + /** + * Gets the specification for the third key attribute + */ + public val attr3: KeyAttrSpec + } /** - * A [KeySpec] for a [kotlin.String]-typed field + * Defines the specification for a key consisting of four attributes + * @param K1 The type of the first key attribute, either [String], [Number], or [ByteArray] + * @param K2 The type of the second key attribute, either [String], [Number], or [ByteArray] + * @param K3 The type of the third key attribute, either [String], [Number], or [ByteArray] + * @param K3 The type of the fourth key attribute, either [String], [Number], or [ByteArray] */ - public interface String : KeySpec + public interface Key4 : KeySpec> { + /** + * Gets the specification for the first key attribute + */ + public val attr1: KeyAttrSpec + + /** + * Gets the specification for the second key attribute + */ + public val attr2: KeyAttrSpec + + /** + * Gets the specification for the third key attribute + */ + public val attr3: KeyAttrSpec + + /** + * Gets the specification for the fourth key attribute + */ + public val attr4: KeyAttrSpec + } public companion object { /** - * Creates a new [ByteArray] key specification - * @param name The name of the key attribute + * Instantiates a new [KeySpec] for a single attribute. Additional attributes may be added with the extension + * methods [thenByteArray], [thenNumber], and [thenString]. + * @param name The name of the attribute */ - public fun ByteArray(name: kotlin.String): KeySpec.ByteArray = ByteArrayImpl(name) + public fun byteArray(name: String): Key1 = KeySpec1Impl(KeyAttrSpec.byteArray(name)) /** - * Creates a new [Number] key specification - * @param name The name of the key attribute + * Instantiates a new [KeySpec] for a single attribute. Additional attributes may be added with the extension + * methods [thenByteArray], [thenNumber], and [thenString]. + * @param N The type of [Number] used for this attribute (e.g., [Int]) + * @param name The name of the attribute */ - public fun Number(name: kotlin.String): KeySpec.Number = NumberImpl(name) + public fun number(name: String): Key1 = KeySpec1Impl(KeyAttrSpec.number(name)) /** - * Creates a new [String] key specification - * @param name The name of the key attribute + * Instantiates a new [KeySpec] for a single attribute. Additional attributes may be added with the extension + * methods [thenByteArray], [thenNumber], and [thenString]. + * @param name The name of the attribute */ - public fun String(name: kotlin.String): KeySpec.String = StringImpl(name) + public fun string(name: String): Key1 = KeySpec1Impl(KeyAttrSpec.string(name)) } +} +/** + * Defines the specification for a single key attribute, including its name and type + * @param K The Kotlin type of the attribute + */ +public interface KeyAttrSpec { /** - * The name of the key attribute + * The DynamoDB name of the key attribute */ - public val name: kotlin.String + public val name: String /** - * Given a value for this key attribute, convert into a field - * @param value The value to use for the key attribute + * The DynamoDB type of the attribute */ - public fun toField(value: K): Pair -} + public val type: ScalarAttributeType -private data class ByteArrayImpl(override val name: String) : KeySpec.ByteArray { - override fun toField(value: ByteArray) = name to AttributeValue.B(value) -} + /** + * Creates a DynamoDB key-value pair for an attribute as a `Pair`, suitable for use in an + * [Item] instance. + */ + public fun toField(value: K): Pair -private data class NumberImpl(override val name: String) : KeySpec.Number { - override fun toField(value: Number) = name to AttributeValue.N(value.toString()) -} + public companion object { + /** + * Instantiates a new binary [KeyAttrSpec] with the given name + * @param name The name of the attribute + */ + public fun byteArray(name: String): KeyAttrSpec = + KeyAttrSpecImpl(name, ScalarAttributeType.B, AttributeValue::B) -private data class StringImpl(override val name: String) : KeySpec.String { - override fun toField(value: String) = name to AttributeValue.S(value) + /** + * Instantiates a new numeric [KeyAttrSpec] with the given name + * @param N The type of [Number] of this attribute (e.g., [Int]) + * @param name The name of the attribute + */ + public fun number(name: String): KeyAttrSpec = + KeyAttrSpecImpl(name, ScalarAttributeType.N) { AttributeValue.N(it.toString()) } + + /** + * Instantiates a new string [KeyAttrSpec] with the given name + * @param name The name of the attribute + */ + public fun string(name: String): KeyAttrSpec = + KeyAttrSpecImpl(name, ScalarAttributeType.S, AttributeValue::S) + } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey1Ext.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey1Ext.kt new file mode 100644 index 00000000000..2441a586997 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey1Ext.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items + +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeySpec2Impl + +/** + * Instantiates a new [KeySpec] that builds from a single attribute into two attributes. Additional attributes may be + * added with the extension methods [thenByteArray], [thenNumber], and [thenString]. + * @param name The name of the second attribute + */ +public fun KeySpec.Key1.thenByteArray(name: String): KeySpec.Key2 = + KeySpec2Impl(attr1, KeyAttrSpec.byteArray(name)) + +/** + * Instantiates a new [KeySpec] that builds from a single attribute into two attributes. Additional attributes may be + * added with the extension methods [thenByteArray], [thenNumber], and [thenString]. + * @param N The type of [Number] used for the second attribute (e.g., [Int]) + * @param name The name of the second attribute + */ +public fun KeySpec.Key1.thenNumber(name: String): KeySpec.Key2 = + KeySpec2Impl(attr1, KeyAttrSpec.number(name)) + +/** + * Instantiates a new [KeySpec] that builds from a single attribute into two attributes. Additional attributes may be + * added with the extension methods [thenByteArray], [thenNumber], and [thenString]. + * @param name The name of the second attribute + */ +public fun KeySpec.Key1.thenString(name: String): KeySpec.Key2 = + KeySpec2Impl(attr1, KeyAttrSpec.string(name)) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey2Ext.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey2Ext.kt new file mode 100644 index 00000000000..1e4be623af4 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey2Ext.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items + +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeySpec3Impl + +/** + * Instantiates a new [KeySpec] that builds from two attributes into three attributes. Additional attributes may be + * added with the extension methods [thenByteArray], [thenNumber], and [thenString]. + * @param name The name of the third attribute + */ +public fun KeySpec.Key2.thenByteArray(name: String): KeySpec.Key3 = + KeySpec3Impl(attr1, attr2, KeyAttrSpec.byteArray(name)) + +/** + * Instantiates a new [KeySpec] that builds from two attributes into three attributes. Additional attributes may be + * added with the extension methods [thenByteArray], [thenNumber], and [thenString]. + * @param N The type of [Number] used for the third attribute (e.g., [Int]) + * @param name The name of the third attribute + */ +public fun KeySpec.Key2.thenNumber(name: String): KeySpec.Key3 = + KeySpec3Impl(attr1, attr2, KeyAttrSpec.number(name)) + +/** + * Instantiates a new [KeySpec] that builds from two attributes into three attributes. Additional attributes may be + * added with the extension methods [thenByteArray], [thenNumber], and [thenString]. + * @param name The name of the third attribute + */ +public fun KeySpec.Key2.thenString(name: String): KeySpec.Key3 = + KeySpec3Impl(attr1, attr2, KeyAttrSpec.string(name)) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey3Ext.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey3Ext.kt new file mode 100644 index 00000000000..28517f07fdf --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpecKey3Ext.kt @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items + +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeySpec4Impl + +/** + * Instantiates a new [KeySpec] that builds from three attributes into four attributes + * @param name The name of the fourth attribute + */ +public fun KeySpec.Key3.thenByteArray(name: String): KeySpec.Key4 = + KeySpec4Impl(attr1, attr2, attr3, KeyAttrSpec.byteArray(name)) + +/** + * Instantiates a new [KeySpec] that builds from three attributes into four attributes + * @param N The type of [Number] used for the fourth attribute (e.g., [Int]) + * @param name The name of the fourth attribute + */ +public fun KeySpec.Key3.thenNumber(name: String): KeySpec.Key4 = + KeySpec4Impl(attr1, attr2, attr3, KeyAttrSpec.number(name)) + +/** + * Instantiates a new [KeySpec] that builds from three attributes into four attributes + * @param name The name of the fourth attribute + */ +public fun KeySpec.Key3.thenString(name: String): KeySpec.Key4 = + KeySpec4Impl(attr1, attr2, attr3, KeyAttrSpec.string(name)) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType.kt new file mode 100644 index 00000000000..78f95c96b3f --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyType.kt @@ -0,0 +1,193 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items + +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeyType1Impl + +/** + * A key value for a table or index. + * + * Keys in DynamoDB may consist of 1, 2, 3, or 4 attributes. This type exists to provide a uniform interface over keys + * of any cardinality to use in `GetItem`, `Query`, and other operations which operate on keys. Where possible, + * convenience methods are added to simplify the most common pattern of a single key attribute. + * + * A [KeyType] consisting of a single attribute value may be created with the [Key] factory function. A [KeyType] + * consisting of additional attribute values may be created by invoking the one of the [invoke] extension function + * overloads. + * + * **Important**: The order of attribute values within a [KeyType] is significant and must reflect the order of the + * attributes defined in the table or index key within DynamoDB as well as related [KeySpec] instances for the same + * table/index. + * + * ## Examples + * + * To create a key value for a single attribute: + * + * ```kotlin + * val keyValue = Key(42) // returns KeyType.Key1 + * ``` + * + * To create a key specification for multiple attributes: + * + * ```kotlin + * val keyValue = Key(42)("ABCD")(1000L) // returns KeyType.Key3 + * ``` + */ +public sealed interface KeyType { + /** + * A key value consisting of a single attribute + * @param K1 The type of the single attribute + */ + public interface Key1 : KeyType { + /** + * The single attribute value + */ + public val value1: K1 + + /** + * Destructuring function that returns the single attribute value + */ + public operator fun component1(): K1 = value1 + } + + /** + * A key value consisting of two attributes + * @param K1 The type of the first attribute + * @param K2 The type of the second attribute + */ + public interface Key2 : KeyType { + /** + * The first attribute value + */ + public val value1: K1 + + /** + * The second attribute value + */ + public val value2: K2 + + /** + * Destructuring function that returns the first attribute value + */ + public operator fun component1(): K1 = value1 + + /** + * Destructuring function that returns the second attribute value + */ + public operator fun component2(): K2 = value2 + } + + /** + * A key value consisting of three attributes + * @param K1 The type of the first attribute + * @param K2 The type of the second attribute + * @param K3 The type of the third attribute + */ + public interface Key3 : KeyType { + /** + * The first attribute value + */ + public val value1: K1 + + /** + * The second attribute value + */ + public val value2: K2 + + /** + * The third attribute value + */ + public val value3: K3 + + /** + * Destructuring function that returns the first attribute value + */ + public operator fun component1(): K1 = value1 + + /** + * Destructuring function that returns the second attribute value + */ + public operator fun component2(): K2 = value2 + + /** + * Destructuring function that returns the third attribute value + */ + public operator fun component3(): K3 = value3 + } + + /** + * A key value consisting of four attributes + * @param K1 The type of the first attribute + * @param K2 The type of the second attribute + * @param K3 The type of the third attribute + * @param K4 The type of the four attribute + */ + public interface Key4 : KeyType { + /** + * The first attribute value + */ + public val value1: K1 + + /** + * The second attribute value + */ + public val value2: K2 + + /** + * The third attribute value + */ + public val value3: K3 + + /** + * The fourth attribute value + */ + public val value4: K4 + + /** + * Destructuring function that returns the first attribute value + */ + public operator fun component1(): K1 = value1 + + /** + * Destructuring function that returns the second attribute value + */ + public operator fun component2(): K2 = value2 + + /** + * Destructuring function that returns the third attribute value + */ + public operator fun component3(): K3 = value3 + + /** + * Destructuring function that returns the fourth attribute value + */ + public operator fun component4(): K4 = value4 + } +} + +/** + * Instantiates a new [KeyType] consisting of a single value. Additional values may be added with one of the [invoke] + * extension function overloads. + * @param value1 The value of the single attribute + */ +@Suppress("FunctionName") +public fun Key(value1: ByteArray): KeyType.Key1 = KeyType1Impl(value1) + +/** + * Instantiates a new [KeyType] consisting of a single value. Additional values may be added with one of the [invoke] + * extension function overloads. + * @param N The type of [Number] used for this attribute (e.g., [Int]) + * @param value1 The value of the single attribute + */ +@Suppress("FunctionName") +public fun Key(value1: N): KeyType.Key1 = KeyType1Impl(value1) + +/** + * Instantiates a new [KeyType] consisting of a single value. Additional values may be added with one of the [invoke] + * extension function overloads. + * @param value1 The value of the single attribute + */ +@Suppress("FunctionName") +public fun Key(value1: String): KeyType.Key1 = KeyType1Impl(value1) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey1Ext.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey1Ext.kt new file mode 100644 index 00000000000..d3284b707a6 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey1Ext.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items + +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeyType2Impl + +/** + * Instantiates a new [KeyType] that builds from a single attribute value into two values. Additional values may be + * added with one of the [invoke] extension function overloads. + * @param value2 The value of the second attribute + */ +public operator fun KeyType.Key1.invoke(value2: ByteArray): KeyType.Key2 = + KeyType2Impl(value1, value2) + +/** + * Instantiates a new [KeyType] that builds from a single attribute value into two values. Additional values may be + * added with one of the [invoke] extension function overloads. + * @param N The type of [Number] used for the second attribute (e.g., [Int]) + * @param value2 The value of the second attribute + */ +public operator fun KeyType.Key1.invoke(value2: N): KeyType.Key2 = + KeyType2Impl(value1, value2) + +/** + * Instantiates a new [KeyType] that builds from a single attribute value into two values. Additional values may be + * added with one of the [invoke] extension function overloads. + * @param value2 The value of the second attribute + */ +public operator fun KeyType.Key1.invoke(value2: String): KeyType.Key2 = + KeyType2Impl(value1, value2) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey2Ext.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey2Ext.kt new file mode 100644 index 00000000000..024ca0ab9f1 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey2Ext.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items + +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeyType3Impl + +/** + * Instantiates a new [KeyType] that builds from two attribute values into three values. Additional values may be added + * with one of the [invoke] extension function overloads. + * @param value3 The value of the third attribute + */ +public operator fun KeyType.Key2.invoke(value3: ByteArray): KeyType.Key3 = + KeyType3Impl(value1, value2, value3) + +/** + * Instantiates a new [KeyType] that builds from two attribute values into three values. Additional values may be added + * with one of the [invoke] extension function overloads. + * @param N The type of [Number] used for the third attribute (e.g., [Int]) + * @param value3 The value of the third attribute + */ +public operator fun KeyType.Key2.invoke(value3: N): KeyType.Key3 = + KeyType3Impl(value1, value2, value3) + +/** + * Instantiates a new [KeyType] that builds from two attribute values into three values. Additional values may be added + * with one of the [invoke] extension function overloads. + * @param value3 The value of the third attribute + */ +public operator fun KeyType.Key2.invoke(value3: String): KeyType.Key3 = + KeyType3Impl(value1, value2, value3) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey3Ext.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey3Ext.kt new file mode 100644 index 00000000000..8dd748cfce3 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/KeyTypeKey3Ext.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items + +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.KeyType4Impl + +/** + * Instantiates a new [KeyType] that builds from three attribute values into four values + * @param value4 The value of the fourth attribute + */ +public operator fun KeyType.Key3.invoke( + value4: ByteArray, +): KeyType.Key4 = KeyType4Impl(value1, value2, value3, value4) + +/** + * Instantiates a new [KeyType] that builds from three attribute values into four values + * @param N The type of [Number] used for the fourth attribute (e.g., [Int]) + * @param value4 The value of the fourth attribute + */ +public operator fun KeyType.Key3.invoke( + value4: N, +): KeyType.Key4 = KeyType4Impl(value1, value2, value3, value4) + +/** + * Instantiates a new [KeyType] that builds from three attribute values into four values + * @param value4 The value of the fourth attribute + */ +public operator fun KeyType.Key3.invoke( + value4: String, +): KeyType.Key4 = KeyType4Impl(value1, value2, value3, value4) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/ItemSchemaImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/ItemSchemaImpl.kt index 789a917e2c0..326f5172f63 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/ItemSchemaImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/ItemSchemaImpl.kt @@ -7,13 +7,14 @@ package aws.sdk.kotlin.hll.dynamodbmapper.items.internal import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemConverter import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType -internal data class ItemSchemaPartitionKeyImpl( +internal data class ItemSchemaPartitionKeyImpl( override val converter: ItemConverter, override val partitionKey: KeySpec, ) : ItemSchema.PartitionKey -internal data class ItemSchemaCompositeKeyImpl( +internal data class ItemSchemaCompositeKeyImpl( override val converter: ItemConverter, override val partitionKey: KeySpec, override val sortKey: KeySpec, diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/KeySpecImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/KeySpecImpl.kt new file mode 100644 index 00000000000..fec0e088301 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/KeySpecImpl.kt @@ -0,0 +1,72 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items.internal + +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyAttrSpec +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType +import aws.sdk.kotlin.hll.dynamodbmapper.model.itemOf +import aws.sdk.kotlin.services.dynamodb.model.AttributeValue +import aws.sdk.kotlin.services.dynamodb.model.ScalarAttributeType + +internal val KeySpec<*>.attrs: List> + get() = when (this) { + is KeySpec.Key1<*> -> listOf(attr1) + is KeySpec.Key2<*, *> -> listOf(attr1, attr2) + is KeySpec.Key3<*, *, *> -> listOf(attr1, attr2, attr3) + is KeySpec.Key4<*, *, *, *> -> listOf(attr1, attr2, attr3, attr4) + } + +internal data class KeySpec1Impl( + override val attr1: KeyAttrSpec, +) : KeySpec.Key1 { + override fun toFields(value: KeyType.Key1) = itemOf( + attr1.toField(value.value1), + ) +} + +internal data class KeySpec2Impl( + override val attr1: KeyAttrSpec, + override val attr2: KeyAttrSpec, +) : KeySpec.Key2 { + override fun toFields(value: KeyType.Key2) = itemOf( + attr1.toField(value.value1), + attr2.toField(value.value2), + ) +} + +internal data class KeySpec3Impl( + override val attr1: KeyAttrSpec, + override val attr2: KeyAttrSpec, + override val attr3: KeyAttrSpec, +) : KeySpec.Key3 { + override fun toFields(value: KeyType.Key3) = itemOf( + attr1.toField(value.value1), + attr2.toField(value.value2), + attr3.toField(value.value3), + ) +} + +internal data class KeySpec4Impl( + override val attr1: KeyAttrSpec, + override val attr2: KeyAttrSpec, + override val attr3: KeyAttrSpec, + override val attr4: KeyAttrSpec, +) : KeySpec.Key4 { + override fun toFields(value: KeyType.Key4) = itemOf( + attr1.toField(value.value1), + attr2.toField(value.value2), + attr3.toField(value.value3), + attr4.toField(value.value4), + ) +} + +internal data class KeyAttrSpecImpl( + override val name: String, + override val type: ScalarAttributeType, + private val attributeValueFactory: (K) -> AttributeValue, +) : KeyAttrSpec { + override fun toField(value: K): Pair = name to attributeValueFactory(value) +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/KeyTypeImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/KeyTypeImpl.kt new file mode 100644 index 00000000000..3a49d0dccb7 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/internal/KeyTypeImpl.kt @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.items.internal + +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType + +internal data class KeyType1Impl(override val value1: K1) : KeyType.Key1 + +internal data class KeyType2Impl(override val value1: K1, override val value2: K2) : KeyType.Key2 + +internal data class KeyType3Impl( + override val value1: K1, + override val value2: K2, + override val value3: K3, +) : KeyType.Key3 + +internal data class KeyType4Impl( + override val value1: K1, + override val value2: K2, + override val value3: K3, + override val value4: K4, +) : KeyType.Key4 + +@Suppress("FunctionName") +internal fun Key(value1: K1): KeyType.Key1 = KeyType1Impl(value1) + +internal val KeyType.values: List + get() = when (this) { + is KeyType.Key1<*> -> listOf(value1) + is KeyType.Key2<*, *> -> listOf(value1, value2) + is KeyType.Key3<*, *, *> -> listOf(value1, value2, value3) + is KeyType.Key4<*, *, *, *> -> listOf(value1, value2, value3, value4) + } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Index.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Index.kt index c297940e477..be37e0b73e3 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Index.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Index.kt @@ -4,6 +4,7 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.model +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.operations.IndexOperations /** @@ -19,19 +20,19 @@ public interface Index : /** * Represents a secondary index whose primary key is a single partition key * @param T The type of objects which will be read from this index - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations */ - public interface PartitionKey : + public interface PartitionKey : Index, PersistenceSpec.PartitionKey /** * Represents a secondary index whose primary key is a composite of a partition key and a sort key * @param T The type of objects which will be read from this index - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations */ - public interface CompositeKey : + public interface CompositeKey : Index, PersistenceSpec.CompositeKey } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec.kt index bd644f55c1d..25e2fd33d1c 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec.kt @@ -5,6 +5,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType /** * Specifies how items can be read from a secondary index @@ -24,7 +25,9 @@ public interface IndexSpec : PersistenceSpec { /** * Specifies how items can be read from a secondary index whose primary key consists of a single partition key */ - public interface PartitionKey : IndexSpec { + public interface PartitionKey : + IndexSpec, + PersistenceSpec.PartitionKey { override val schema: ItemSchema.PartitionKey } @@ -32,7 +35,9 @@ public interface IndexSpec : PersistenceSpec { * Specifies how items can be read from a secondary index whose primary key consists of a composite of a partition * key and a sort key */ - public interface CompositeKey : IndexSpec { + public interface CompositeKey : + IndexSpec, + PersistenceSpec.CompositeKey { override val schema: ItemSchema.CompositeKey } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/ItemSource.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/ItemSource.kt index 4cac2295474..948d6803d20 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/ItemSource.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/ItemSource.kt @@ -4,6 +4,7 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.model +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.operations.ItemSourceOperations /** @@ -17,9 +18,9 @@ public interface ItemSource : * Represents a source of DynamoDB items (such as a table or secondary index) whose primary key is a single * partition key * @param T The type of objects which will be read from and/or written to this item source - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations */ - public interface PartitionKey : + public interface PartitionKey : ItemSource, PersistenceSpec.PartitionKey @@ -27,10 +28,10 @@ public interface ItemSource : * Represents a source of DynamoDB items (such as a table or secondary index) whose primary key is a composite of a * partition key and a sort key * @param T The type of objects which will be read from and/or written to this item source - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations */ - public interface CompositeKey : + public interface CompositeKey : ItemSource, PersistenceSpec.CompositeKey } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec.kt index 640d36bd952..c9fd53d30c3 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/PersistenceSpec.kt @@ -6,6 +6,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType /** * Specifies how items can be read from and written to a specific DynamoDB location (such as a table or a secondary @@ -27,7 +28,7 @@ public interface PersistenceSpec { * Specifies how items can be read from and written to a specific DynamoDB location (such as a table or a secondary * index) whose primary key consists of a single partition key */ - public interface PartitionKey : PersistenceSpec { + public interface PartitionKey : PersistenceSpec { override val schema: ItemSchema.PartitionKey } @@ -35,7 +36,7 @@ public interface PersistenceSpec { * Specifies how items can be read from and written to a specific DynamoDB location (such as a table or a secondary * index) whose primary key consists of a composite of a partition key and a sort key */ - public interface CompositeKey : PersistenceSpec { + public interface CompositeKey : PersistenceSpec { override val schema: ItemSchema.CompositeKey } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Table.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Table.kt index 9ca08c1423d..c924e534d6f 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Table.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/Table.kt @@ -5,6 +5,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.operations.TableOperations /** @@ -20,9 +21,9 @@ public interface Table : /** * Represents a table whose primary key is a single partition key * @param T The type of objects which will be read from and/or written to this table - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations */ - public interface PartitionKey : + public interface PartitionKey : Table, ItemSource.PartitionKey { // TODO reimplement operations to use pipeline, extension functions where appropriate, docs, etc. @@ -32,10 +33,10 @@ public interface Table : /** * Represents a table whose primary key is a composite of a partition key and a sort key * @param T The type of objects which will be read from and/or written to this table - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations */ - public interface CompositeKey : + public interface CompositeKey : Table, ItemSource.CompositeKey { // TODO reimplement operations to use pipeline, extension functions where appropriate, docs, etc. @@ -45,11 +46,11 @@ public interface Table : /** * Get an [Index] reference for performing secondary index operations * @param T The type of objects which will be read from to this index - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations * @param name The name of the index * @param schema The [ItemSchema] which describes the index, its keys, and how items are converted */ - public fun getIndex( + public fun getIndex( name: String, schema: ItemSchema.PartitionKey, ): Index.PartitionKey @@ -57,12 +58,12 @@ public interface Table : /** * Get an [Index] reference for performing secondary index operations * @param T The type of objects which will be read from this index - * @param PK The type of the partition key property, either [String], [Number], or [ByteArray] - * @param SK The type of the sort key property, either [String], [Number], or [ByteArray] + * @param PK The type of the partition key property, either [KeyType] or one of its specific derivations + * @param SK The type of the sort key property, either [KeyType] or one of its specific derivations * @param name The name of the index * @param schema The [ItemSchema] which describes the index, its keys, and how items are converted */ - public fun getIndex( + public fun getIndex( name: String, schema: ItemSchema.CompositeKey, ): Index.CompositeKey diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/TableGetItemExt.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/TableGetItemExt.kt new file mode 100644 index 00000000000..ed61d18f08f --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/TableGetItemExt.kt @@ -0,0 +1,138 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.model + +import aws.sdk.kotlin.hll.dynamodbmapper.items.Key +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType + +/** + * Gets an item from this table with the given partition key value + * @param T The type of objects which will be read from and/or written to this table + * @param partitionKey The partition key value of the item + */ +public suspend fun Table.PartitionKey>.getItem(partitionKey: ByteArray): T? = + getItem(Key(partitionKey)) + +/** + * Gets an item from this table with the given partition key value + * @param T The type of objects which will be read from and/or written to this table + * @param N The type of [Number] of the partition key + * @param partitionKey The partition key value of the item + */ +public suspend fun Table.PartitionKey>.getItem(partitionKey: N): T? = + getItem(Key(partitionKey)) + +/** + * Gets an item from this table with the given partition key value + * @param T The type of objects which will be read from and/or written to this table + * @param partitionKey The partition key value of the item + */ +public suspend fun Table.PartitionKey>.getItem(partitionKey: String): T? = + getItem(Key(partitionKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: ByteArray, + sortKey: ByteArray, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param N2 The type of [Number] of the sort key + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: ByteArray, + sortKey: N2, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: ByteArray, + sortKey: String, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param N1 The type of [Number] of the partition key + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: N1, + sortKey: ByteArray, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param N1 The type of [Number] of the partition key + * @param N2 The type of [Number] of the sort key + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: N1, + sortKey: N2, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param N1 The type of [Number] of the partition key + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: N1, + sortKey: String, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: String, + sortKey: ByteArray, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param N2 The type of [Number] of the sort key + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: String, + sortKey: N2, +): T? = getItem(Key(partitionKey), Key(sortKey)) + +/** + * Gets an item from this table with the given partition key and sort key values + * @param T The type of objects which will be read from and/or written to this table + * @param partitionKey The partition key value of the item + * @param sortKey The sort key value of the item + */ +public suspend fun Table.CompositeKey, KeyType.Key1>.getItem( + partitionKey: String, + sortKey: String, +): T? = getItem(Key(partitionKey), Key(sortKey)) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec.kt index 0c63ed3818a..f00930fd710 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/TableSpec.kt @@ -5,6 +5,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType /** * Specifies how items can be read from and written to a table @@ -19,15 +20,19 @@ public interface TableSpec : PersistenceSpec { /** * Specifies how items can be read from or written to a table whose primary key consists of a single partition key */ - public interface PartitionKey : TableSpec { + public interface PartitionKey : + TableSpec, + PersistenceSpec.PartitionKey { override val schema: ItemSchema.PartitionKey } /** - * Specifies how items can be read from or written to a table whose primary key consists of a composite of a + * Specifies how items can be read from or written to a table whose primary key consists of a composite of a * partition key and a sort key */ - public interface CompositeKey : TableSpec { + public interface CompositeKey : + TableSpec, + PersistenceSpec.CompositeKey { override val schema: ItemSchema.CompositeKey } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexImpl.kt index bd2acd02445..c93aebaf7b1 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexImpl.kt @@ -6,12 +6,13 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model.internal import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.model.Index import aws.sdk.kotlin.hll.dynamodbmapper.model.IndexSpec import aws.sdk.kotlin.hll.dynamodbmapper.operations.IndexOperations import aws.sdk.kotlin.hll.dynamodbmapper.operations.IndexOperationsImpl -internal fun indexImpl( +internal fun indexImpl( mapper: DynamoDbMapper, tableName: String, indexName: String, @@ -25,7 +26,7 @@ internal fun indexImpl( IndexOperations by opsImpl { } } -internal fun indexImpl( +internal fun indexImpl( mapper: DynamoDbMapper, tableName: String, indexName: String, diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexSpecImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexSpecImpl.kt index 00f58a55867..dde3879f325 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexSpecImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/IndexSpecImpl.kt @@ -6,16 +6,17 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model.internal import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.model.IndexSpec -internal data class IndexSpecPartitionKeyImpl( +internal data class IndexSpecPartitionKeyImpl( override val mapper: DynamoDbMapper, override val tableName: String, override val indexName: String, override val schema: ItemSchema.PartitionKey, ) : IndexSpec.PartitionKey -internal data class IndexSpecCompositeKeyImpl( +internal data class IndexSpecCompositeKeyImpl( override val mapper: DynamoDbMapper, override val tableName: String, override val indexName: String, diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableImpl.kt index c570e943212..7a9fa347352 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableImpl.kt @@ -6,10 +6,10 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model.internal import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.model.Index import aws.sdk.kotlin.hll.dynamodbmapper.model.Table import aws.sdk.kotlin.hll.dynamodbmapper.model.TableSpec -import aws.sdk.kotlin.hll.dynamodbmapper.model.itemOf import aws.sdk.kotlin.hll.dynamodbmapper.operations.* import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.Interceptor import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.LReqContext @@ -17,7 +17,7 @@ import aws.sdk.kotlin.services.dynamodb.model.AttributeValue import aws.sdk.kotlin.services.dynamodb.model.GetItemRequest as LowLevelGetItemRequest import aws.sdk.kotlin.services.dynamodb.model.GetItemResponse as LowLevelGetItemResponse -internal fun tableImpl( +internal fun tableImpl( mapper: DynamoDbMapper, name: String, schema: ItemSchema.PartitionKey, @@ -29,18 +29,18 @@ internal fun tableImpl( TableSpec.PartitionKey by specImpl, TableOperations by opsImpl { - override fun getIndex( + override fun getIndex( name: String, schema: ItemSchema.PartitionKey, ): Index.PartitionKey = indexImpl(mapper, tableName, name, schema) - override fun getIndex( + override fun getIndex( name: String, schema: ItemSchema.CompositeKey, ): Index.CompositeKey = indexImpl(mapper, tableName, name, schema) override suspend fun getItem(partitionKey: PK): T? { - val keyItem = itemOf(schema.partitionKey.toField(partitionKey)) + val keyItem = schema.partitionKey.toFields(partitionKey) val interceptor = KeyInsertionInterceptor(keyItem) val op = getItemOperation(specImpl).let { it.copy( @@ -53,7 +53,7 @@ internal fun tableImpl( } } -internal fun tableImpl( +internal fun tableImpl( mapper: DynamoDbMapper, name: String, schema: ItemSchema.CompositeKey, @@ -65,21 +65,18 @@ internal fun tableImpl( TableSpec.CompositeKey by specImpl, TableOperations by opsImpl { - override fun getIndex( + override fun getIndex( name: String, schema: ItemSchema.PartitionKey, ): Index.PartitionKey = indexImpl(mapper, tableName, name, schema) - override fun getIndex( + override fun getIndex( name: String, schema: ItemSchema.CompositeKey, ): Index.CompositeKey = indexImpl(mapper, tableName, name, schema) override suspend fun getItem(partitionKey: PK, sortKey: SK): T? { - val keyItem = itemOf( - schema.partitionKey.toField(partitionKey), - schema.sortKey.toField(sortKey), - ) + val keyItem = schema.partitionKey.toFields(partitionKey) + schema.sortKey.toFields(sortKey) val interceptor = KeyInsertionInterceptor(keyItem) val op = getItemOperation(specImpl).let { it.copy( diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableSpecImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableSpecImpl.kt index 0e491e037cc..5c8f7975abd 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableSpecImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/model/internal/TableSpecImpl.kt @@ -6,15 +6,16 @@ package aws.sdk.kotlin.hll.dynamodbmapper.model.internal import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyType import aws.sdk.kotlin.hll.dynamodbmapper.model.TableSpec -internal data class TableSpecPartitionKeyImpl( +internal data class TableSpecPartitionKeyImpl( override val mapper: DynamoDbMapper, override val tableName: String, override val schema: ItemSchema.PartitionKey, ) : TableSpec.PartitionKey -internal data class TableSpecCompositeKeyImpl( +internal data class TableSpecCompositeKeyImpl( override val mapper: DynamoDbMapper, override val tableName: String, override val schema: ItemSchema.CompositeKey, diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilterTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilterTest.kt index 8be4ba6109c..b06fc272d8f 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilterTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilterTest.kt @@ -15,8 +15,8 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class KeyFilterTest { - private val singleKeySchema = ItemSchema(DummyConverter, KeySpec.String("primary")) - private val compositeSchema = ItemSchema(DummyConverter, KeySpec.String("primary"), KeySpec.Number("secondary")) + private val singleKeySchema = ItemSchema(DummyConverter, KeySpec.string("primary")) + private val compositeSchema = ItemSchema(DummyConverter, KeySpec.string("primary"), KeySpec.number("secondary")) @Test fun testSingleKeySchema() { @@ -29,7 +29,7 @@ class KeyFilterTest { @Test fun testSingleKeySchemaWithErroneousSortKey() { - val kf = KeyFilter("foo") { sortKey eq 2 } + val kf = KeyFilter("foo", { sortKey eq 2 }) assertFailsWith { kf.toExpression(singleKeySchema) @@ -38,7 +38,7 @@ class KeyFilterTest { @Test fun testCompositeSchema() { - val kf = KeyFilter("foo") { sortKey lte 10 } + val kf = KeyFilter("foo", { sortKey lte 10 }) val actual = kf.toExpression(compositeSchema) val expected = FilterImpl.run { and( diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapperTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapperTest.kt index 476f92a242c..4ada61f90aa 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapperTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbMapperTest.kt @@ -39,7 +39,7 @@ class DynamoDbMapperTest : DdbLocalTest() { AttributeDescriptor("bar", DummyData::bar, DummyData::bar::set, NumberValueConverters.Int), ) - private val dummySchema = ItemSchema(dummyConverter, KeySpec.String("foo"), KeySpec.Number("bar")) + private val dummySchema = ItemSchema(dummyConverter, KeySpec.string("foo"), KeySpec.number("bar")) } @BeforeAll diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemTest.kt index ce9749252c3..6da0a125e32 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemTest.kt @@ -30,7 +30,7 @@ class DeleteItemTest : DdbLocalTest() { AttributeDescriptor("id", Item::id, Item::id::set, StringValueConverter), AttributeDescriptor("value", Item::value, Item::value::set, NumberValueConverters.Int), ) - private val schema = ItemSchema(converter, KeySpec.String("id")) + private val schema = ItemSchema(converter, KeySpec.string("id")) } @BeforeAll diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemTest.kt index 9fb730e62be..fe8f42c3622 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemTest.kt @@ -9,6 +9,7 @@ import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec import aws.sdk.kotlin.hll.dynamodbmapper.items.SimpleItemConverter import aws.sdk.kotlin.hll.dynamodbmapper.model.Table +import aws.sdk.kotlin.hll.dynamodbmapper.model.getItem import aws.sdk.kotlin.hll.dynamodbmapper.model.itemOf import aws.sdk.kotlin.hll.dynamodbmapper.testutils.DdbLocalTest import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.NumberValueConverters @@ -32,7 +33,7 @@ class GetItemTest : DdbLocalTest() { AttributeDescriptor("id", PkItem::id, PkItem::id::set, NumberValueConverters.Int), AttributeDescriptor("value", PkItem::value, PkItem::value::set, StringValueConverter), ) - private val pkSchema = ItemSchema(pkConverter, KeySpec.Number("id")) + private val pkSchema = ItemSchema(pkConverter, KeySpec.number("id")) private data class CkItem(var id: String = "", var version: Int = 0, var value: String = "") @@ -43,7 +44,7 @@ class GetItemTest : DdbLocalTest() { AttributeDescriptor("version", CkItem::version, CkItem::version::set, NumberValueConverters.Int), AttributeDescriptor("value", CkItem::value, CkItem::value::set, StringValueConverter), ) - private val ckSchema = ItemSchema(ckConverter, KeySpec.String("id"), KeySpec.Number("version")) + private val ckSchema = ItemSchema(ckConverter, KeySpec.string("id"), KeySpec.number("version")) } @BeforeAll diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PaginatedScanTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PaginatedScanTest.kt index 6fb7fc60d35..0ac1c0ee577 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PaginatedScanTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PaginatedScanTest.kt @@ -51,7 +51,7 @@ class PaginatedScanTest : DdbLocalTest() { AttributeDescriptor("description", Card::description, Card::description::set, StringValueConverter), ) - private val schema = ItemSchema(converter, KeySpec.String("suit"), KeySpec.Number("rank")) + private val schema = ItemSchema(converter, KeySpec.string("suit"), KeySpec.number("rank")) private val allCards = listOf("Spades", "Clubs", "Hearts", "Diamonds").flatMap { suit -> (2..14).map { rank -> diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemTest.kt index 95d4f9d99ae..6088abdac76 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemTest.kt @@ -28,7 +28,7 @@ class PutItemTest : DdbLocalTest() { AttributeDescriptor("id", Item::id, Item::id::set, StringValueConverter), AttributeDescriptor("value", Item::value, Item::value::set, NumberValueConverters.Int), ) - private val schema = ItemSchema(converter, KeySpec.String("id")) + private val schema = ItemSchema(converter, KeySpec.string("id")) } @BeforeAll diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryMultiAttrKeyTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryMultiAttrKeyTest.kt new file mode 100644 index 00000000000..400cb287c8f --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryMultiAttrKeyTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.operations + +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.KeyFilter +import aws.sdk.kotlin.hll.dynamodbmapper.items.* +import aws.sdk.kotlin.hll.dynamodbmapper.model.itemOf +import aws.sdk.kotlin.hll.dynamodbmapper.testutils.DdbLocalTest +import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.NumberValueConverters +import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.StringValueConverter +import aws.sdk.kotlin.services.dynamodb.model.DynamoDbException +import io.kotest.core.annotation.Ignored +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.test.runTest +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +@Ignored // DynamoDB Local 2.6.1 does not support multi-attribute keys yet +class QueryMultiAttrKeyTest : DdbLocalTest() { + companion object { + private const val TABLE_NAME = "query-multi-attr-keys-test" + private const val INDEX_NAME = "multi-attr-keys-index" + + private data class Event( + var eventId: String = "", // Table PK + var companyId: Int = 0, // Index PK[0] + var department: String = "", // Index PK[1] + var category: String = "", // Index SK[0] + var timestamp: Long = 0L, // Index SK[1] + var description: String = "", + ) + + private val eventConverter = SimpleItemConverter( + ::Event, + { this }, + AttributeDescriptor("eventId", Event::eventId, Event::eventId::set, StringValueConverter), + AttributeDescriptor("companyId", Event::companyId, Event::companyId::set, NumberValueConverters.Int), + AttributeDescriptor("department", Event::department, Event::department::set, StringValueConverter), + AttributeDescriptor("category", Event::category, Event::category::set, StringValueConverter), + AttributeDescriptor("timestamp", Event::timestamp, Event::timestamp::set, NumberValueConverters.Long), + AttributeDescriptor("description", Event::description, Event::description::set, StringValueConverter), + ) + + private val tableSchema = eventConverter.withKeySpec(KeySpec.string("eventId")) + + private val indexSchema = eventConverter.withKeySpec( + KeySpec.number("companyId").thenString("department"), + KeySpec.string("category").thenNumber<_, Long>("timestamp"), + ) + } + + @BeforeAll + fun setUp() = runTest { + createTable( + name = TABLE_NAME, + schema = tableSchema, + gsis = mapOf(INDEX_NAME to indexSchema), + lsis = mapOf(), + items = listOf( + itemOf( + "eventId" to "05deb8cd-1b27-428c-b685-12890e4b9144", + "companyId" to 128, + "department" to "Billing", + "category" to "Personnel changes", + "timestamp" to 1_765_569_132_000_000_000L, + "description" to "New employee joined!", + ), + itemOf( + "eventId" to "46af0204-d738-4c6f-8a78-b08da8196f6c", + "companyId" to 128, + "department" to "Billing", + "category" to "Personnel changes", + "timestamp" to 1_765_569_133_000_000_000L, + "description" to "Employee retired", + ), + ), + ) + } + + @Test + fun testQueryIndexAllKeys() = runTest { + val index = mapper().getTable(TABLE_NAME, tableSchema).getIndex(INDEX_NAME, indexSchema) + + val descriptions = index.queryPaginated { + keyCondition = KeyFilter( + Key(128)("Billing"), + { sortKey eq "Personnel changes" }, + { sortKey eq 1_765_569_132_000_000_000L }, + ) + }.items().map { it.description }.toList() + + assertEquals(listOf("New employee joined!"), descriptions) + } + + @Test + fun testQueryIndexPartialKeys() = runTest { + val index = mapper().getTable(TABLE_NAME, tableSchema).getIndex(INDEX_NAME, indexSchema) + + val descriptions = index.queryPaginated { + keyCondition = KeyFilter( + Key(128)("Billing"), + { sortKey eq "Personnel changes" }, + ) + }.items().map { it.description }.toList() + + assertEquals(listOf("New employee joined!", "Employee retired"), descriptions) + } + + @Test + fun testQueryIndexInsufficientKeys() = runTest { + val index = mapper().getTable(TABLE_NAME, tableSchema).getIndex(INDEX_NAME, indexSchema) + + val items = index.queryPaginated { + keyCondition = KeyFilter(Key(128)) // Should fail because PK has two attributes but we only set one value + }.items() + + assertFailsWith { items.toList() } + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt index e894ea1300a..7657958143d 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt @@ -41,8 +41,8 @@ class QueryTest : DdbLocalTest() { AttributeDescriptor("tenureYears", NamedEmp::tenureYears, NamedEmp::tenureYears::set, NumberValueConverters.Int), ) - private val namedEmpSchema = ItemSchema(empConverter, KeySpec.String("companyId"), KeySpec.String("empId")) - private val empsByNameSchema = ItemSchema(empConverter, KeySpec.String("companyId"), KeySpec.String("name")) + private val namedEmpSchema = ItemSchema(empConverter, KeySpec.string("companyId"), KeySpec.string("empId")) + private val empsByNameSchema = ItemSchema(empConverter, KeySpec.string("companyId"), KeySpec.string("name")) private data class TitleEmp( var title: String = "", @@ -60,7 +60,7 @@ class QueryTest : DdbLocalTest() { AttributeDescriptor("companyId", TitleEmp::companyId, TitleEmp::companyId::set, StringValueConverter), ) - private val titleSchema = ItemSchema(titleConverter, KeySpec.String("title"), KeySpec.String("name")) + private val titleSchema = ItemSchema(titleConverter, KeySpec.string("title"), KeySpec.string("name")) } @BeforeAll @@ -145,7 +145,7 @@ class QueryTest : DdbLocalTest() { val table = mapper.getTable(TABLE_NAME, namedEmpSchema) val items = table.queryPaginated { - keyCondition = KeyFilter("foo-corp") { sortKey startsWith "AB0" } + keyCondition = KeyFilter("foo-corp", { sortKey startsWith "AB0" }) }.items().toList() val expected = listOf( diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanTest.kt index 4e2fc01d6b2..e483703085c 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanTest.kt @@ -40,7 +40,7 @@ class ScanTest : DdbLocalTest() { AttributeDescriptor("released", Product::released, Product::released::set, InstantValueConverter.Iso8601), ) - private val schema = ItemSchema(converter, KeySpec.Number("id")) + private val schema = ItemSchema(converter, KeySpec.number("id")) } @BeforeAll diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt index efc5bb76ff5..c1f1df9aa0c 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt @@ -179,7 +179,7 @@ private val fooConverter = object : ItemConverter { itemOf("foo" to AttributeValue.S(obj.value)) } } -private val fooSchema = fooConverter.withKeySpec(KeySpec.String("foo")) +private val fooSchema = fooConverter.withKeySpec(KeySpec.string("foo")) private data class HFooRequest(val foo: Foo) private data class LFooRequest(val table: String, val foo: Item) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/testutils/DdbClientExtensions.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/testutils/DdbClientExtensions.kt index a37c8ca7ca4..8d4faea99f1 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/testutils/DdbClientExtensions.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/testutils/DdbClientExtensions.kt @@ -5,8 +5,10 @@ package aws.sdk.kotlin.hll.dynamodbmapper.testutils import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema +import aws.sdk.kotlin.hll.dynamodbmapper.items.KeyAttrSpec import aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec import aws.sdk.kotlin.hll.dynamodbmapper.items.SimpleItemConverter +import aws.sdk.kotlin.hll.dynamodbmapper.items.internal.attrs import aws.sdk.kotlin.hll.dynamodbmapper.model.Item import aws.sdk.kotlin.hll.dynamodbmapper.model.itemOf import aws.sdk.kotlin.services.dynamodb.DynamoDbClient @@ -30,7 +32,9 @@ suspend fun DynamoDbClient.createTable( gsis: Map>, lsis: Map>, ) { - val keys = schema.toKeys() + val partitionKeyAttrs = schema.partitionKeyAttrs() + val sortKeyAttrs = schema.sortKeyAttrs() + val throughput = ProvisionedThroughput { // provisioned throughput is required but ignored by DDB Local so just use dummy values readCapacityUnits = 1 @@ -42,10 +46,15 @@ suspend fun DynamoDbClient.createTable( attributeDefinitions = deriveAttributes(schema, gsis, lsis) - keySchema = keys.mapIndexed { index, key -> + keySchema = partitionKeyAttrs.map { attr -> + KeySchemaElement { + attributeName = attr.name + keyType = KeyType.Hash + } + } + sortKeyAttrs.map { attr -> KeySchemaElement { - attributeName = key.name - keyType = if (index == 0) KeyType.Hash else KeyType.Range + attributeName = attr.name + keyType = KeyType.Range } } @@ -114,15 +123,18 @@ private fun deriveAttributes( gsis: Map>, lsis: Map>, ): List { - val allKeys = schema.toKeys() + gsis.values.flatMap { it.toKeys() } + lsis.values.flatMap { it.toKeys() } - return allKeys - .associateBy(KeySpec<*>::name) - .map { (name, key) -> - AttributeDefinition { - attributeName = name - attributeType = key.toScalarAttributeType() - } - } + val keyAttrs = schema.allKeyAttrs() + + gsis.values.flatMap { it.allKeyAttrs() } + + lsis.values.flatMap { it.allKeyAttrs() } + + return keyAttrs + .distinctBy { it.name } + .map { it.toAttributeDefinition() } +} + +private fun KeyAttrSpec<*>.toAttributeDefinition() = AttributeDefinition { + attributeName = name + attributeType = type } /** @@ -136,21 +148,25 @@ private val ItemSchema<*>.allAttributeNames: Set } /** - * Converts this [KeySpec] to a [ScalarAttributeType] + * Extracts the partition and sort key attributes from this [ItemSchema] as a list + */ +private fun ItemSchema<*>.allKeyAttrs() = partitionKeyAttrs() + sortKeyAttrs() + +/** + * Extracts the partition key attributes from this [ItemSchema] as a list */ -private fun KeySpec<*>.toScalarAttributeType() = when (this) { - is KeySpec.ByteArray -> ScalarAttributeType.B - is KeySpec.Number -> ScalarAttributeType.N - is KeySpec.String -> ScalarAttributeType.S +private fun ItemSchema<*>.partitionKeyAttrs() = when (this) { + is ItemSchema.PartitionKey<*, *> -> partitionKey.attrs + is ItemSchema.CompositeKey<*, *, *> -> partitionKey.attrs } /** - * Extracts the [KeySpec] instances from this [ItemSchema] + * Extracts the sort key attributes from this [ItemSchema] as a list. The returned list will be empty if the schema has + * no sort key. */ -private fun ItemSchema<*>.toKeys() = when (this) { - is ItemSchema.CompositeKey<*, *, *> -> listOf(partitionKey, sortKey) - is ItemSchema.PartitionKey<*, *> -> listOf(partitionKey) - else -> error("Unknown schema type ${this::class}") +private fun ItemSchema<*>.sortKeyAttrs() = when (this) { + is ItemSchema.PartitionKey<*, *> -> listOf() + is ItemSchema.CompositeKey<*, *, *> -> sortKey.attrs } /** diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGenerator.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGenerator.kt index b34295a00d4..d1819e590c2 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGenerator.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGenerator.kt @@ -115,6 +115,13 @@ public interface CodeGenerator { */ public fun docs(template: String, vararg args: Any) + /** + * Writes a newline + */ + public fun write() { + write("") + } + /** * Writes a line of text, including a terminating newline (i.e., `\n`) * @param template The string template or literal to append