Skip to content

Commit 5a52082

Browse files
committed
don't sync from main
1 parent 983bcdb commit 5a52082

File tree

10 files changed

+149
-5
lines changed

10 files changed

+149
-5
lines changed

hll/dynamodb-mapper/dynamodb-mapper-annotations/api/dynamodb-mapper-annotations.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/Dyn
22
public abstract fun name ()Ljava/lang/String;
33
}
44

5+
public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbAttributeConverter : java/lang/annotation/Annotation {
6+
public abstract fun converter ()Ljava/lang/Class;
7+
}
8+
59
public abstract interface annotation class aws/sdk/kotlin/hll/dynamodbmapper/DynamoDbIgnore : java/lang/annotation/Annotation {
610
}
711

hll/dynamodb-mapper/dynamodb-mapper-annotations/build.gradle.kts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,14 @@
66
description = "DynamoDbMapper annotations"
77
extra["displayName"] = "AWS :: SDK :: Kotlin :: HLL :: DynamoDbMapper :: Annotations"
88
extra["moduleName"] = "aws.sdk.kotlin.hll.dynamodbmapper.annotations"
9+
10+
kotlin {
11+
sourceSets {
12+
commonMain {
13+
dependencies {
14+
// For ValueConverter
15+
implementation(project(":hll:dynamodb-mapper:dynamodb-mapper"))
16+
}
17+
}
18+
}
19+
}

hll/dynamodb-mapper/dynamodb-mapper-annotations/common/src/aws/sdk/kotlin/hll/dynamodbmapper/Annotations.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,28 @@
44
*/
55
package aws.sdk.kotlin.hll.dynamodbmapper
66

7+
import aws.sdk.kotlin.hll.dynamodbmapper.values.ValueConverter
8+
import kotlin.reflect.KClass
9+
710
/**
811
* Specifies the attribute name for a property in a [DynamoDbItem]-annotated class/interface. If this annotation is not
912
* included then the attribute name matches the property name.
1013
*/
1114
@Target(AnnotationTarget.PROPERTY)
1215
public annotation class DynamoDbAttribute(val name: String)
1316

17+
/**
18+
* Specifies the type of [ValueConverter] to be used when processing this attribute.
19+
*/
20+
public annotation class DynamoDbAttributeConverter(val converter: KClass<out ValueConverter<*>>)
21+
1422
/**
1523
* Specifies that this class/interface describes an item type in a table. All public properties of this type will be mapped to
1624
* attributes unless they are explicitly ignored.
1725
* @param converterName The fully qualified name of the item converter to be used for converting this class/interface.
1826
* If not set, one will be automatically generated.
1927
*/
20-
// FIXME Update to take a KClass<ItemConverter>, which will require splitting codegen modules due to a circular dependency
28+
// FIXME Update to take a KClass<ItemConverter>?
2129
@Target(AnnotationTarget.CLASS)
2230
public annotation class DynamoDbItem(val converterName: String = "")
2331

hll/dynamodb-mapper/dynamodb-mapper-schema-codegen/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies {
2222
implementation(project(":hll:hll-codegen"))
2323
implementation(project(":hll:dynamodb-mapper:dynamodb-mapper-annotations"))
2424
implementation(project(":hll:dynamodb-mapper:dynamodb-mapper-codegen"))
25+
implementation(project(":hll:dynamodb-mapper:dynamodb-mapper")) // for ValueConverter.kt
2526

2627
testImplementation(libs.junit.jupiter)
2728
testImplementation(libs.junit.jupiter.params)

hll/dynamodb-mapper/dynamodb-mapper-schema-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/HighLevelRenderer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal class HighLevelRenderer(
4242
attributes,
4343
)
4444

45-
val annotation = SchemaRenderer(annotated, renderCtx)
45+
val annotation = SchemaRenderer(logger, annotated, renderCtx)
4646
annotation.render()
4747
}
4848
}

hll/dynamodb-mapper/dynamodb-mapper-schema-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.rendering
66

7+
import aws.sdk.kotlin.hll.codegen.core.ImportDirective
78
import aws.sdk.kotlin.hll.codegen.model.*
89
import aws.sdk.kotlin.hll.codegen.rendering.BuilderRenderer
910
import aws.sdk.kotlin.hll.codegen.rendering.RenderContext
@@ -18,6 +19,7 @@ import com.google.devtools.ksp.KspExperimental
1819
import com.google.devtools.ksp.getAnnotationsByType
1920
import com.google.devtools.ksp.getConstructors
2021
import com.google.devtools.ksp.isAnnotationPresent
22+
import com.google.devtools.ksp.processing.KSPLogger
2123
import com.google.devtools.ksp.symbol.*
2224

2325
/**
@@ -27,6 +29,7 @@ import com.google.devtools.ksp.symbol.*
2729
*/
2830
@OptIn(KspExperimental::class)
2931
internal class SchemaRenderer(
32+
private val logger: KSPLogger,
3033
private val classDeclaration: KSClassDeclaration,
3134
private val ctx: RenderContext,
3235
) : RendererBase(ctx, "${classDeclaration.qualifiedName!!.getShortName()}Schema") {
@@ -148,6 +151,7 @@ internal class SchemaRenderer(
148151
}
149152

150153
private fun renderAttributeDescriptor(prop: KSPropertyDeclaration) {
154+
logger.info("Rendering an attribute descriptor for ${prop.simpleName.asString()}")
151155
withBlock("#T(", "),", MapperTypes.Items.AttributeDescriptor) {
152156
write("#S,", prop.ddbName) // key
153157
write("#L,", "$className::${prop.name}") // getter
@@ -160,8 +164,25 @@ internal class SchemaRenderer(
160164
}
161165

162166
// converter
163-
renderValueConverter(prop.type.resolve())
164-
write(",")
167+
// KSP requires extra work to get a class argument out of an annotation, can't just use getAnnotationsByType
168+
// https://slack-chats.kotlinlang.org/t/8480301/hello-again-how-do-you-get-a-kclass-out-from-an-annotation-a
169+
val attributeValueConverterFqn = prop.annotations
170+
.singleOrNull { it.annotationType.resolve().declaration.qualifiedName?.asString() == DynamoDbAttributeConverter::class.qualifiedName }
171+
?.arguments
172+
?.single()
173+
?.value
174+
?.let { it as? KSType }
175+
?.declaration
176+
?.qualifiedName
177+
?.asString()
178+
179+
attributeValueConverterFqn?.let {
180+
imports += ImportDirective(it)
181+
write("$it(),")
182+
} ?: run {
183+
renderValueConverter(prop.type.resolve())
184+
write(",")
185+
}
165186
}
166187
}
167188

@@ -229,7 +250,6 @@ internal class SchemaRenderer(
229250
Types.Kotlin.UInt -> MapperTypes.Values.Scalars.UIntValueConverter
230251
Types.Kotlin.UShort -> MapperTypes.Values.Scalars.UShortValueConverter
231252
Types.Kotlin.ULong -> MapperTypes.Values.Scalars.ULongValueConverter
232-
233253
else -> error("Unsupported attribute type $type")
234254
},
235255
)

hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ dependencies {
2424
implementation(libs.ksp.gradle.plugin)
2525

2626
implementation(project(":hll:hll-codegen")) // for RenderOptions
27+
implementation(project(":hll:dynamodb-mapper:dynamodb-mapper")) // for ValueConverter
2728
implementation(project(":hll:dynamodb-mapper:dynamodb-mapper-schema-codegen")) // for AnnotationsProcessorOptions
2829
implementation(libs.smithy.kotlin.runtime.core) // for AttributeKey
2930

hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,4 +569,41 @@ class SchemaGeneratorPluginTest {
569569
""".trimIndent(),
570570
)
571571
}
572+
573+
@Test
574+
fun testDynamoDbAttributeConverter() {
575+
createClassFile("attribute-converter/Employee")
576+
createClassFile("attribute-converter/HealthcareConverter")
577+
578+
val result = runner.build()
579+
assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), result.task(":build")?.outcome)
580+
581+
val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/EmployeeSchema.kt")
582+
assertTrue(schemaFile.exists())
583+
584+
val schemaContents = schemaFile.readText()
585+
586+
assertContains(schemaContents, "import org.example.OccupationConverter")
587+
assertContains(
588+
schemaContents,
589+
""" AttributeDescriptor(
590+
"occupation",
591+
Employee::occupation,
592+
Employee::occupation::set,
593+
org.example.OccupationConverter(),
594+
),""",
595+
)
596+
597+
// Test cross-package converter
598+
assertContains(schemaContents, "import a.different.pkg.HealthcareConverter")
599+
assertContains(
600+
schemaContents,
601+
""" AttributeDescriptor(
602+
"healthcare",
603+
Employee::healthcare,
604+
Employee::healthcare::set,
605+
a.different.pkg.HealthcareConverter(),
606+
),""",
607+
)
608+
}
572609
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.example
7+
8+
import a.different.pkg.HealthcareConverter
9+
import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbAttributeConverter
10+
import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem
11+
import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey
12+
import aws.sdk.kotlin.hll.dynamodbmapper.values.ValueConverter
13+
import aws.sdk.kotlin.hll.mapping.core.converters.MonoConverter
14+
import aws.sdk.kotlin.services.dynamodb.model.AttributeValue
15+
16+
@DynamoDbItem
17+
data class Employee(
18+
@DynamoDbPartitionKey
19+
var id: Int = 1,
20+
var givenName: String = "Johnny",
21+
var surname: String = "Appleseed",
22+
23+
@property:DynamoDbAttributeConverter(OccupationConverter::class)
24+
var occupation: Occupation = Occupation("Student", 0),
25+
26+
@property:DynamoDbAttributeConverter(HealthcareConverter::class)
27+
var healthcare: Healthcare = Healthcare(false),
28+
)
29+
30+
data class Occupation(val title: String, val salary: Int)
31+
data class Healthcare(val enrolled: Boolean)
32+
33+
class OccupationConverter : ValueConverter<Occupation> {
34+
override val right = MonoConverter<Occupation, AttributeValue> { AttributeValue.S(it.title + "#" + it.salary) }
35+
36+
override val left = MonoConverter<AttributeValue, Occupation> {
37+
val content = it.asS()
38+
val (title, salary) = content.split("#")
39+
Occupation(title, salary.toInt())
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package a.different.pkg
7+
8+
import aws.sdk.kotlin.hll.dynamodbmapper.values.ValueConverter
9+
import aws.sdk.kotlin.hll.mapping.core.converters.MonoConverter
10+
import aws.sdk.kotlin.services.dynamodb.model.AttributeValue
11+
import org.example.Healthcare
12+
13+
class HealthcareConverter : ValueConverter<Healthcare> {
14+
override val right = MonoConverter<Healthcare, AttributeValue> { AttributeValue.S(it.enrolled.toString()) }
15+
16+
override val left = MonoConverter<AttributeValue, Healthcare> {
17+
val content = it.asS()
18+
val enrolled = (content == "true")
19+
Healthcare(enrolled)
20+
}
21+
}

0 commit comments

Comments
 (0)