From 42efb77162b432a46283d4d11db02c3f28d51b5d Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Tue, 10 Sep 2024 10:35:28 -0400 Subject: [PATCH 01/12] Add DynamoDbIgnore and DynamoDbItemConverter --- .../api/dynamodb-mapper-annotations.api | 7 +++ .../kotlin/hll/dynamodbmapper/Annotations.kt | 15 ++++++- .../annotations/rendering/SchemaRenderer.kt | 31 ++++++++++--- .../plugins/SchemaGeneratorPluginTest.kt | 45 +++++++++++++++++++ .../src/test/resources/IgnoredProperty.kt | 17 +++++++ .../CustomItemConverter.kt | 40 +++++++++++++++++ .../custom-item-converter/CustomUser.kt | 15 +++++++ 7 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt create mode 100644 hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt create mode 100644 hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api b/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api index 98bcd3deee7..5f428c625e3 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api @@ -2,9 +2,16 @@ public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/Dyn public abstract fun name ()Ljava/lang/String; } +public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbIgnore : java/lang/annotation/Annotation { +} + public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbItem : java/lang/annotation/Annotation { } +public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbItemConverter : java/lang/annotation/Annotation { + public abstract fun qualifiedName ()Ljava/lang/String; +} + public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbPartitionKey : java/lang/annotation/Annotation { } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt index 0bef80bcf83..de56cee5834 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt @@ -12,7 +12,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper public annotation class DynamoDbAttribute(val name: String) /** - * Specifies that this class/interface describes an item type in a table. All properties of this type will be mapped to + * Specifies that this class/interface describes an item type in a table. All public properties of this type will be mapped to * attributes unless they are explicitly ignored. */ @Target(AnnotationTarget.CLASS) @@ -31,3 +31,16 @@ public annotation class DynamoDbPartitionKey */ @Target(AnnotationTarget.PROPERTY) public annotation class DynamoDbSortKey + +/** + * Specifies that this property should be ignored by the DynamoDb Mapper. + */ +@Target(AnnotationTarget.PROPERTY) +public annotation class DynamoDbIgnore + +/** + * Specifies that a custom ItemConverter should be used to convert this class/interface. + * @param qualifiedName The fully qualified name of the item converter. + */ +@Target(AnnotationTarget.CLASS) +public annotation class DynamoDbItemConverter(val qualifiedName: String) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index 71efcd19e36..084a15ed3fb 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -10,9 +10,7 @@ import aws.sdk.kotlin.hll.codegen.model.TypeRef import aws.sdk.kotlin.hll.codegen.model.Types import aws.sdk.kotlin.hll.codegen.rendering.* import aws.sdk.kotlin.hll.codegen.util.visibility -import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbAttribute -import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey -import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbSortKey +import aws.sdk.kotlin.hll.dynamodbmapper.* import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.AnnotationsProcessorOptions import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.GenerateBuilderClasses import aws.sdk.kotlin.hll.dynamodbmapper.codegen.model.MapperTypes @@ -31,6 +29,7 @@ import com.google.devtools.ksp.symbol.Modifier * @param classDeclaration the [KSClassDeclaration] of the class * @param ctx the [RenderContext] of the renderer */ +@OptIn(KspExperimental::class) internal class SchemaRenderer( private val classDeclaration: KSClassDeclaration, private val ctx: RenderContext, @@ -42,7 +41,11 @@ internal class SchemaRenderer( private val converterName = "${className}Converter" private val schemaName = "${className}Schema" - private val properties = classDeclaration.getAllProperties().filterNot { it.modifiers.contains(Modifier.PRIVATE) } + @OptIn(KspExperimental::class) + private val properties = classDeclaration.getAllProperties() + .filterNot { it.modifiers.contains(Modifier.PRIVATE) } + .filterNot { it.isAnnotationPresent(DynamoDbIgnore::class) } + private val annotatedProperties = properties.mapNotNull(AnnotatedClassProperty.Companion::from) init { @@ -57,6 +60,17 @@ internal class SchemaRenderer( private val partitionKeyProp = annotatedProperties.single { it.isPk } private val sortKeyProp = annotatedProperties.singleOrNull { it.isSk } + private val itemConverter: Type + get() = run { + val qualifiedName = classDeclaration.getAnnotationsByType(DynamoDbItemConverter::class).singleOrNull()?.qualifiedName + + return qualifiedName?.let { + val pkg = qualifiedName.substringBeforeLast(".") + val shortName = qualifiedName.removePrefix("$pkg.") + TypeRef(pkg, shortName) + } ?: TypeRef(ctx.pkg, "${className}Converter") + } + /** * We skip rendering a class builder if: * - the user has configured GenerateBuilders to WHEN_REQUIRED (default value) AND @@ -75,8 +89,13 @@ internal class SchemaRenderer( if (shouldRenderBuilder) { renderBuilder() } - renderItemConverter() + + if (!classDeclaration.isAnnotationPresent(DynamoDbItemConverter::class)) { + renderItemConverter() + } + renderSchema() + if (ctx.attributes[AnnotationsProcessorOptions.GenerateGetTableMethodAttribute]) { renderGetTable() } @@ -140,7 +159,7 @@ internal class SchemaRenderer( } withBlock("#Lobject #L : #T {", "}", ctx.attributes.visibility, schemaName, schemaType) { - write("override val converter : #1L = #1L", converterName) + write("override val converter : #1T = #1T", itemConverter) write("override val partitionKey: #T = #T(#S)", MapperTypes.Items.keySpec(partitionKeyProp.keySpec), partitionKeyProp.keySpecType, partitionKeyProp.name) if (sortKeyProp != null) { write("override val sortKey: #T = #T(#S)", MapperTypes.Items.keySpec(sortKeyProp.keySpec), sortKeyProp.keySpecType, sortKeyProp.name) 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 34f99ab6eca..2c537d06539 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 @@ -357,4 +357,49 @@ class SchemaGeneratorPluginTest { val testResult = runner.withArguments("test").build() assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), testResult.task(":test")?.outcome) } + + @Test + fun testDynamoDbIgnore() { + createClassFile("IgnoredProperty") + + val result = runner.build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), result.task(":build")?.outcome) + + val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/IgnoredPropertySchema.kt") + assertTrue(schemaFile.exists()) + + val schemaContents = schemaFile.readText() + + assertContains(schemaContents, "public class IgnoredProperty") + assertContains(schemaContents, "public var id: Int? = null") + assertContains(schemaContents, "public var givenName: String? = null") + assertContains(schemaContents, "public var surname: String? = null") + assertContains(schemaContents, "public var age: Int? = null") + assertContains(schemaContents, "public fun build(): IgnoredProperty") + + // ssn is annotated with DynamoDbIgnore + assertFalse(schemaContents.contains("public var ssn: String? = null")) + } + + @Test + fun testDynamoDbItemConverter() { + createClassFile("custom-item-converter/CustomUser") + createClassFile("custom-item-converter/CustomItemConverter", "src/main/kotlin/my/custom/item/converter") + + val result = runner.build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), result.task(":build")?.outcome) + + // Schema should not exist + val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/CustomUserSchema.kt") + assertTrue(schemaFile.exists()) + + val schemaContents = schemaFile.readText() + assertFalse(schemaContents.contains("public object CustomUserItemConverter : ItemConverter by SimpleItemConverter")) + assertContains(schemaContents, """ + public object CustomUserSchema : ItemSchema.PartitionKey { + override val converter : MyCustomUserConverter = MyCustomUserConverter + override val partitionKey: KeySpec = aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec.Number("id") + } + """.trimIndent()) + } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt new file mode 100644 index 00000000000..81e8c116cdd --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt @@ -0,0 +1,17 @@ +package org.example + +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbAttribute +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbIgnore +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey + +@DynamoDbItem +public data class IgnoredProperty( + @DynamoDbPartitionKey var id: Int, + @DynamoDbAttribute("fName") var givenName: String, + @DynamoDbAttribute("lName") var surname: String, + var age: Int, + + @DynamoDbIgnore + var ssn: String? = null +) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt new file mode 100644 index 00000000000..4215fe46837 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt @@ -0,0 +1,40 @@ +package my.custom.item.converter + +import org.example.CustomUser +import aws.sdk.kotlin.hll.dynamodbmapper.items.SimpleItemConverter +import aws.sdk.kotlin.hll.dynamodbmapper.items.AttributeDescriptor +import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemConverter +import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.IntConverter +import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.StringConverter + +public object MyCustomUserConverter : ItemConverter by SimpleItemConverter( + builderFactory = { CustomUser() }, + build = { this }, + descriptors = arrayOf( + AttributeDescriptor( + "id", + CustomUser::id, + CustomUser::id::set, + IntConverter + ), + AttributeDescriptor( + "myCustomFirstName", + CustomUser::givenName, + CustomUser::givenName::set, + StringConverter + ), + AttributeDescriptor( + "myCustomLastName", + CustomUser::surname, + CustomUser::surname::set, + StringConverter + ), + AttributeDescriptor( + "myCustomAge", + CustomUser::age, + CustomUser::age::set, + IntConverter + ), + ), +) + diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt new file mode 100644 index 00000000000..6edf650aea5 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt @@ -0,0 +1,15 @@ +package org.example + +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbAttribute +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItemConverter +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey + +@DynamoDbItem +@DynamoDbItemConverter("my.custom.item.converter.MyCustomUserConverter") +public data class CustomUser( + @DynamoDbPartitionKey var id: Int = 1, + var givenName: String = "Johnny", + var surname: String = "Appleseed", + var age: Int = 0, +) From c8c44db3e43f676f5ea8b0167da5c9e48c60c4e3 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Tue, 10 Sep 2024 10:35:46 -0400 Subject: [PATCH 02/12] ktlint --- .../plugins/SchemaGeneratorPluginTest.kt | 7 +++++-- .../src/test/resources/IgnoredProperty.kt | 2 +- .../custom-item-converter/CustomItemConverter.kt | 13 ++++++------- .../resources/custom-item-converter/CustomUser.kt | 1 - 4 files changed, 12 insertions(+), 11 deletions(-) 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 2c537d06539..f66c955a3ad 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 @@ -395,11 +395,14 @@ class SchemaGeneratorPluginTest { val schemaContents = schemaFile.readText() assertFalse(schemaContents.contains("public object CustomUserItemConverter : ItemConverter by SimpleItemConverter")) - assertContains(schemaContents, """ + assertContains( + schemaContents, + """ public object CustomUserSchema : ItemSchema.PartitionKey { override val converter : MyCustomUserConverter = MyCustomUserConverter override val partitionKey: KeySpec = aws.sdk.kotlin.hll.dynamodbmapper.items.KeySpec.Number("id") } - """.trimIndent()) + """.trimIndent(), + ) } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt index 81e8c116cdd..ff9c091258a 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/IgnoredProperty.kt @@ -13,5 +13,5 @@ public data class IgnoredProperty( var age: Int, @DynamoDbIgnore - var ssn: String? = null + var ssn: String? = null, ) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt index 4215fe46837..6c9f12cde5d 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomItemConverter.kt @@ -1,11 +1,11 @@ package my.custom.item.converter -import org.example.CustomUser -import aws.sdk.kotlin.hll.dynamodbmapper.items.SimpleItemConverter import aws.sdk.kotlin.hll.dynamodbmapper.items.AttributeDescriptor import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemConverter +import aws.sdk.kotlin.hll.dynamodbmapper.items.SimpleItemConverter import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.IntConverter import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.StringConverter +import org.example.CustomUser public object MyCustomUserConverter : ItemConverter by SimpleItemConverter( builderFactory = { CustomUser() }, @@ -15,26 +15,25 @@ public object MyCustomUserConverter : ItemConverter by SimpleItemCon "id", CustomUser::id, CustomUser::id::set, - IntConverter + IntConverter, ), AttributeDescriptor( "myCustomFirstName", CustomUser::givenName, CustomUser::givenName::set, - StringConverter + StringConverter, ), AttributeDescriptor( "myCustomLastName", CustomUser::surname, CustomUser::surname::set, - StringConverter + StringConverter, ), AttributeDescriptor( "myCustomAge", CustomUser::age, CustomUser::age::set, - IntConverter + IntConverter, ), ), ) - diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt index 6edf650aea5..4a69c884a7c 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt @@ -1,6 +1,5 @@ package org.example -import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbAttribute import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItemConverter import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey From 81ba731395fc89a2eb6f1cf5dfacda4db375ca5f Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Tue, 10 Sep 2024 10:37:09 -0400 Subject: [PATCH 03/12] update kdoc --- .../common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt index de56cee5834..625b77aef40 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt @@ -33,7 +33,7 @@ public annotation class DynamoDbPartitionKey public annotation class DynamoDbSortKey /** - * Specifies that this property should be ignored by the DynamoDb Mapper. + * Specifies that this property should be ignored during mapping. */ @Target(AnnotationTarget.PROPERTY) public annotation class DynamoDbIgnore From 8cc97bd0db478ee06cefb48a1e06c8215b8b548b Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Tue, 10 Sep 2024 10:38:46 -0400 Subject: [PATCH 04/12] Remove extra opt-in --- .../codegen/annotations/rendering/SchemaRenderer.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index 084a15ed3fb..1a1ef9e3d62 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -41,7 +41,6 @@ internal class SchemaRenderer( private val converterName = "${className}Converter" private val schemaName = "${className}Schema" - @OptIn(KspExperimental::class) private val properties = classDeclaration.getAllProperties() .filterNot { it.modifiers.contains(Modifier.PRIVATE) } .filterNot { it.isAnnotationPresent(DynamoDbIgnore::class) } From fe6323c57f155167c21ae9ec28301195b7f2c925 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Wed, 11 Sep 2024 10:33:45 -0400 Subject: [PATCH 05/12] Calculate `itemConverter` once --- .../codegen/annotations/rendering/SchemaRenderer.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index 1a1ef9e3d62..5c2d1363b7b 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -59,15 +59,14 @@ internal class SchemaRenderer( private val partitionKeyProp = annotatedProperties.single { it.isPk } private val sortKeyProp = annotatedProperties.singleOrNull { it.isSk } - private val itemConverter: Type - get() = run { + private val itemConverter: Type = run { val qualifiedName = classDeclaration.getAnnotationsByType(DynamoDbItemConverter::class).singleOrNull()?.qualifiedName - return qualifiedName?.let { - val pkg = qualifiedName.substringBeforeLast(".") - val shortName = qualifiedName.removePrefix("$pkg.") + qualifiedName?.let { + val pkg = it.substringBeforeLast(".") + val shortName = it.removePrefix("$pkg.") TypeRef(pkg, shortName) - } ?: TypeRef(ctx.pkg, "${className}Converter") + } ?: TypeRef(ctx.pkg, converterName) } /** From db5b3fbd54489d11ed98d91e234e5f74de5bcac4 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Wed, 11 Sep 2024 10:34:27 -0400 Subject: [PATCH 06/12] Remove class-level optin to KspExperimental --- .../codegen/annotations/rendering/SchemaRenderer.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index 5c2d1363b7b..ac1718e6518 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -29,7 +29,6 @@ import com.google.devtools.ksp.symbol.Modifier * @param classDeclaration the [KSClassDeclaration] of the class * @param ctx the [RenderContext] of the renderer */ -@OptIn(KspExperimental::class) internal class SchemaRenderer( private val classDeclaration: KSClassDeclaration, private val ctx: RenderContext, @@ -41,6 +40,7 @@ internal class SchemaRenderer( private val converterName = "${className}Converter" private val schemaName = "${className}Schema" + @OptIn(KspExperimental::class) private val properties = classDeclaration.getAllProperties() .filterNot { it.modifiers.contains(Modifier.PRIVATE) } .filterNot { it.isAnnotationPresent(DynamoDbIgnore::class) } @@ -59,6 +59,7 @@ internal class SchemaRenderer( private val partitionKeyProp = annotatedProperties.single { it.isPk } private val sortKeyProp = annotatedProperties.singleOrNull { it.isSk } + @OptIn(KspExperimental::class) private val itemConverter: Type = run { val qualifiedName = classDeclaration.getAnnotationsByType(DynamoDbItemConverter::class).singleOrNull()?.qualifiedName @@ -83,6 +84,7 @@ internal class SchemaRenderer( !(!alwaysGenerateBuilders && hasAllMutableMembers && hasZeroArgConstructor) } + @OptIn(KspExperimental::class) override fun generate() { if (shouldRenderBuilder) { renderBuilder() From af4c868bd31d391ec02b42d401a0d347fae0cdcc Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Wed, 11 Sep 2024 10:36:59 -0400 Subject: [PATCH 07/12] Fluent style call chaining --- .../codegen/annotations/rendering/SchemaRenderer.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index ac1718e6518..f7c43776bc8 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -41,9 +41,9 @@ internal class SchemaRenderer( private val schemaName = "${className}Schema" @OptIn(KspExperimental::class) - private val properties = classDeclaration.getAllProperties() - .filterNot { it.modifiers.contains(Modifier.PRIVATE) } - .filterNot { it.isAnnotationPresent(DynamoDbIgnore::class) } + private val properties = classDeclaration + .getAllProperties() + .filterNot { it.modifiers.contains(Modifier.PRIVATE) || it.isAnnotationPresent(DynamoDbIgnore::class) } private val annotatedProperties = properties.mapNotNull(AnnotatedClassProperty.Companion::from) From b8c4df67a42d9f30413fad33c82dd7509ef28cef Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Wed, 11 Sep 2024 10:55:26 -0400 Subject: [PATCH 08/12] Addressing PR feedback --- .../api/dynamodb-mapper-annotations.api | 5 +--- .../kotlin/hll/dynamodbmapper/Annotations.kt | 11 ++------ .../annotations/rendering/SchemaRenderer.kt | 28 +++++++++---------- .../custom-item-converter/CustomUser.kt | 4 +-- 4 files changed, 19 insertions(+), 29 deletions(-) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api b/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api index 5f428c625e3..8f5c7313892 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api @@ -6,10 +6,7 @@ public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/Dyn } public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbItem : java/lang/annotation/Annotation { -} - -public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbItemConverter : java/lang/annotation/Annotation { - public abstract fun qualifiedName ()Ljava/lang/String; + public abstract fun converterName ()Ljava/lang/String; } public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbPartitionKey : java/lang/annotation/Annotation { diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt index 625b77aef40..ced048cf650 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt @@ -14,9 +14,11 @@ public annotation class DynamoDbAttribute(val name: String) /** * Specifies that this class/interface describes an item type in a table. All public properties of this type will be mapped to * attributes unless they are explicitly ignored. + * @param converterName The fully qualified name of the item converter to be used for converting this class/interface. + * If not set, one will be automatically generated. */ @Target(AnnotationTarget.CLASS) -public annotation class DynamoDbItem +public annotation class DynamoDbItem(val converterName: String = "") /** * Specifies that this property is the primary key for the item. Every top-level [DynamoDbItem] to be used in a table @@ -37,10 +39,3 @@ public annotation class DynamoDbSortKey */ @Target(AnnotationTarget.PROPERTY) public annotation class DynamoDbIgnore - -/** - * Specifies that a custom ItemConverter should be used to convert this class/interface. - * @param qualifiedName The fully qualified name of the item converter. - */ -@Target(AnnotationTarget.CLASS) -public annotation class DynamoDbItemConverter(val qualifiedName: String) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index f7c43776bc8..7114762aa44 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -40,6 +40,18 @@ internal class SchemaRenderer( private val converterName = "${className}Converter" private val schemaName = "${className}Schema" + @OptIn(KspExperimental::class) + private val dynamoDbItemAnnotation = classDeclaration.getAnnotationsByType(DynamoDbItem::class).single() + + private val itemConverter: Type = dynamoDbItemAnnotation + .converterName + .takeIf { it.isNotBlank() } + ?.let { + val pkg = it.substringBeforeLast(".") + val shortName = it.removePrefix("$pkg.") + TypeRef(pkg, shortName) + } ?: TypeRef(ctx.pkg, converterName) + @OptIn(KspExperimental::class) private val properties = classDeclaration .getAllProperties() @@ -59,19 +71,8 @@ internal class SchemaRenderer( private val partitionKeyProp = annotatedProperties.single { it.isPk } private val sortKeyProp = annotatedProperties.singleOrNull { it.isSk } - @OptIn(KspExperimental::class) - private val itemConverter: Type = run { - val qualifiedName = classDeclaration.getAnnotationsByType(DynamoDbItemConverter::class).singleOrNull()?.qualifiedName - - qualifiedName?.let { - val pkg = it.substringBeforeLast(".") - val shortName = it.removePrefix("$pkg.") - TypeRef(pkg, shortName) - } ?: TypeRef(ctx.pkg, converterName) - } - /** - * We skip rendering a class builder if: + * Skip rendering a class builder if: * - the user has configured GenerateBuilders to WHEN_REQUIRED (default value) AND * - the class has all mutable members AND * - the class has a zero-arg constructor @@ -84,13 +85,12 @@ internal class SchemaRenderer( !(!alwaysGenerateBuilders && hasAllMutableMembers && hasZeroArgConstructor) } - @OptIn(KspExperimental::class) override fun generate() { if (shouldRenderBuilder) { renderBuilder() } - if (!classDeclaration.isAnnotationPresent(DynamoDbItemConverter::class)) { + if (dynamoDbItemAnnotation.converterName.isBlank()) { renderItemConverter() } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt index 4a69c884a7c..75fe3234cdb 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/custom-item-converter/CustomUser.kt @@ -1,11 +1,9 @@ package org.example import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem -import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItemConverter import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey -@DynamoDbItem -@DynamoDbItemConverter("my.custom.item.converter.MyCustomUserConverter") +@DynamoDbItem("my.custom.item.converter.MyCustomUserConverter") public data class CustomUser( @DynamoDbPartitionKey var id: Int = 1, var givenName: String = "Johnny", From 2736ffd3e82bf41c1927e4876ddde7a89fd76cbb Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Wed, 11 Sep 2024 11:46:28 -0400 Subject: [PATCH 09/12] latest --- .../src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt index ced048cf650..59efa1909bf 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt @@ -39,3 +39,12 @@ public annotation class DynamoDbSortKey */ @Target(AnnotationTarget.PROPERTY) public annotation class DynamoDbIgnore + +@Target(AnnotationTarget.PROPERTY) +public annotation class DynamoDbGSIPartitionKey + +@Target(AnnotationTarget.PROPERTY) +public annotation class DynamoDbGSISortKey + +//@Target(AnnotationTarget.PROPERTY) +//public annotation class DynamoDbLSISortKey From 9fd0ac66e9265b535d0fdccfce286bc5dd1d77ba Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Thu, 12 Sep 2024 14:43:37 -0400 Subject: [PATCH 10/12] add a FIXME --- .../common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt index 59efa1909bf..23c8c777122 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt @@ -17,6 +17,7 @@ public annotation class DynamoDbAttribute(val name: String) * @param converterName The fully qualified name of the item converter to be used for converting this class/interface. * If not set, one will be automatically generated. */ +// FIXME Update to take a KClass, which will require splitting codegen modules due to a circular dependency @Target(AnnotationTarget.CLASS) public annotation class DynamoDbItem(val converterName: String = "") From c677e2f28e5616d71dc204711bb6a4dfa29d386a Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Thu, 12 Sep 2024 14:51:03 -0400 Subject: [PATCH 11/12] Remove GSIs --- .../src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt index 23c8c777122..e102c7f49c4 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt @@ -40,12 +40,3 @@ public annotation class DynamoDbSortKey */ @Target(AnnotationTarget.PROPERTY) public annotation class DynamoDbIgnore - -@Target(AnnotationTarget.PROPERTY) -public annotation class DynamoDbGSIPartitionKey - -@Target(AnnotationTarget.PROPERTY) -public annotation class DynamoDbGSISortKey - -//@Target(AnnotationTarget.PROPERTY) -//public annotation class DynamoDbLSISortKey From a4714694d3ff81ed380d5e89ad126fb495b91fa8 Mon Sep 17 00:00:00 2001 From: Matas Lauzadis Date: Thu, 12 Sep 2024 14:51:58 -0400 Subject: [PATCH 12/12] Remove misleading comment --- .../hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt | 1 - 1 file changed, 1 deletion(-) 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 f66c955a3ad..b376421c1f0 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 @@ -389,7 +389,6 @@ class SchemaGeneratorPluginTest { val result = runner.build() assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), result.task(":build")?.outcome) - // Schema should not exist val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/CustomUserSchema.kt") assertTrue(schemaFile.exists())