Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hll/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ val projectsToIgnore = listOf(
"dynamodb-mapper-codegen",
"dynamodb-mapper-ops-codegen",
"dynamodb-mapper-schema-codegen",
"dynamodb-mapper-schema-generator-plugin-test",
).filter { it in subprojects.map { it.name }.toSet() } // Some projects may not be in the build depending on bootstrapping

apiValidation {
ignoredProjects += projectsToIgnore
nonPublicMarkers += "aws.smithy.kotlin.runtime.GeneratedApi"
}

// Configure Dokka for subprojects
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ internal fun Structure.toHighLevel(pkg: String): Structure {
val nullable = llMember.type.nullable

val hlMember = when (val behavior = llMember.codegenBehavior) {
MemberCodegenBehavior.PassThrough -> llMember
MemberCodegenBehavior.PassThrough -> llMember.copy(
attributes = attributes + (ModelAttributes.GeneratedApi to true),
)

MemberCodegenBehavior.MapAll, MemberCodegenBehavior.MapKeys ->
llMember.copy(type = TypeVar("T", nullable))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import aws.sdk.kotlin.hll.codegen.rendering.BuilderRenderer
import aws.sdk.kotlin.hll.codegen.rendering.RenderContext
import aws.sdk.kotlin.hll.codegen.rendering.RenderOptions
import aws.sdk.kotlin.hll.codegen.rendering.Visibility
import aws.sdk.kotlin.hll.codegen.util.generatedAnnotation
import aws.sdk.kotlin.hll.codegen.util.plus

/**
Expand Down Expand Up @@ -66,10 +67,15 @@ internal class DataTypeGenerator(
private val structure: Structure,
) : CodeGenerator by generator {
fun generate() {
generatedAnnotation(structure)
withBlock("public interface #T {", "}", structure.type) {
generatedAnnotation(structure)
write("public companion object { }") // leave room for future expansion
blankLine()
members { write("public val #L: #T", name, type) }
members {
generatedAnnotation(this)
write("public val #L: #T", name, type)
}
}
blankLine()

Expand All @@ -85,10 +91,11 @@ internal class DataTypeGenerator(
val builderCtx = ctx.copy(
attributes = ctx.attributes + (RenderOptions.VisibilityAttribute to Visibility.PUBLIC),
)
val builderName = BuilderRenderer.builderName(structure.type)
BuilderRenderer(this, structure.type, implType, structure.members, builderCtx).render()
val builderName = BuilderRenderer.builderName(structure)
BuilderRenderer(this, structure, implType, structure.members, builderCtx).render()

blankLine()
generatedAnnotation(structure)
withBlock(
"public fun #1L#2T.toBuilder(): #3L#4L = #3L#4L().apply {",
"}",
Expand All @@ -101,6 +108,7 @@ internal class DataTypeGenerator(
}

blankLine()
generatedAnnotation(structure)
withBlock(
"public fun #1L#2T.copy(block: #3L#4L.() -> Unit): #2T =",
"",
Expand All @@ -113,6 +121,7 @@ internal class DataTypeGenerator(
}

blankLine()
generatedAnnotation(structure)
withBlock(
"public fun #L#L(block: #L#L.() -> Unit): #T =",
"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ internal class OperationsTypeRenderer(
.forEach(::renderDslOp)

private fun renderDslOp(op: Operation) {
val builderType = BuilderRenderer.builderType(op.request.type)
val builderType = BuilderRenderer.builderType(op.request)
val generics = op.request.genericVars().asParamsList(" ")

if (op.paginationInfo != null) renderManualPaginationAnnotation(op) else blankLine()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,9 @@ internal class PaginatorRenderer(
private val paginationInfo = requireNotNull(op.paginationInfo) { "Operation ${op.name} is not paginatable" }
private val name = paginatorName(op)

private val requestType = op.request.type
private val requestBuilderType = BuilderRenderer.builderType(requestType)
private val responseType = op.response.type

private val requestBuilderType = BuilderRenderer.builderType(op.request)
private val itemFlowType = Types.Kotlinx.Coroutines.Flow.flow(TypeVar("T"))
private val pageFlowType = Types.Kotlinx.Coroutines.Flow.flow(responseType)
private val pageFlowType = Types.Kotlinx.Coroutines.Flow.flow(op.response.type)

fun render() {
if (forResponses) {
Expand Down Expand Up @@ -147,7 +144,7 @@ internal class PaginatorRenderer(
"#L(initialRequest: #T): #T = #T {",
"}",
name,
requestType,
op.request.type,
pageFlowType,
Types.Kotlinx.Coroutines.Flow.flow,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import aws.sdk.kotlin.hll.codegen.model.*
import aws.sdk.kotlin.hll.codegen.rendering.BuilderRenderer
import aws.sdk.kotlin.hll.codegen.rendering.RenderContext
import aws.sdk.kotlin.hll.codegen.rendering.RendererBase
import aws.sdk.kotlin.hll.codegen.util.generatedAnnotation
import aws.sdk.kotlin.hll.codegen.util.plus
import aws.sdk.kotlin.hll.codegen.util.visibility
import aws.sdk.kotlin.hll.dynamodbmapper.*
import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.AnnotationsProcessorOptions
Expand All @@ -32,6 +34,7 @@ internal class SchemaRenderer(
private val ctx: RenderContext,
) : RendererBase(ctx, "${classDeclaration.qualifiedName!!.getShortName()}Schema") {
private val className = classDeclaration.qualifiedName!!.getShortName()
private val classStructure = Structure.from(classDeclaration)
private val classType = Type.from(classDeclaration)

private val builderName = "${className}Builder"
Expand Down Expand Up @@ -104,12 +107,23 @@ internal class SchemaRenderer(
}

private fun renderBuilder() {
val members = properties.map(Member.Companion::from).toSet()
BuilderRenderer(this, classType, classType, members, ctx).render()
val builderCtx = ctx.copy(
attributes = ctx.attributes + (ModelAttributes.GeneratedApi to true),
)
val members = properties.map(Member::from).toSet()
BuilderRenderer(this, classStructure, classType, members, builderCtx).render()
}

private fun renderItemConverter() {
withBlock("#Lobject #L : #T by #T(", ")", ctx.attributes.visibility, converterName, MapperTypes.Items.itemConverter(classType), MapperTypes.Items.SimpleItemConverter) {
generatedAnnotation()
withBlock(
"#Lobject #L : #T by #T(",
")",
ctx.attributes.visibility,
converterName,
MapperTypes.Items.itemConverter(classType),
MapperTypes.Items.SimpleItemConverter,
) {
if (shouldRenderBuilder) {
write("builderFactory = ::#L,", builderName)
write("build = #L::build,", builderName)
Expand All @@ -133,6 +147,7 @@ internal class SchemaRenderer(
*/
private fun renderValueConverter() {
// TODO Offer alternate serialization options besides AttributeValue.M?
generatedAnnotation()
write(
"#Lval #L : #T = #T.#T(#T)",
ctx.attributes.visibility,
Expand Down Expand Up @@ -304,17 +319,18 @@ internal class SchemaRenderer(
MapperTypes.Items.itemSchemaCompositeKey(classType, partitionKeyTypeRefs, sortKeyTypeRefs)
}

generatedAnnotation()
withBlock("#Lobject #L : #T {", "}", ctx.attributes.visibility, schemaName, schemaType) {
write("override val converter: #1T = #1T", itemConverter)

writeInline("override val partitionKey: #T = ", MapperTypes.Items.keySpec(partitionKeyTypeRefs))
keySpecInstantiation(partitionKeyProps)
write()
newline()

if (sortKeyProps.isNotEmpty()) {
writeInline("override val sortKey: #T = ", MapperTypes.Items.keySpec(sortKeyTypeRefs))
keySpecInstantiation(sortKeyProps)
write()
newline()
}
}

Expand Down Expand Up @@ -369,6 +385,7 @@ internal class SchemaRenderer(
MapperTypes.Model.tableCompositeKey(classType, partitionKeyTypeRefs, sortKeyTypeRefs)
}

generatedAnnotation()
val fnName = "get${className}Table"
write(
"#Lfun #T.#L(name: String): #T = #L(name, #L)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,43 @@ class SchemaGeneratorPluginTest {
val schemaContents = schemaFile.readText()

// Builder
assertContains(schemaContents, "public class UserBuilder")
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(): User")
assertContains(
schemaContents,
"""
@GeneratedApi
public class UserBuilder {
@GeneratedApi
public var id: Int? = null
@GeneratedApi
public var givenName: String? = null
@GeneratedApi
public var surname: String? = null
@GeneratedApi
public var age: Int? = null

@GeneratedApi
public fun build(): User {
val id = requireNotNull(id) { "Missing value for id" }
val givenName = requireNotNull(givenName) { "Missing value for givenName" }
val surname = requireNotNull(surname) { "Missing value for surname" }
val age = requireNotNull(age) { "Missing value for age" }

return User(
id,
givenName,
surname,
age,
)
}
}
""".trimIndent(),
)

// Converter
assertContains(
schemaContents,
"""
@GeneratedApi
public object UserConverter : ItemConverter<User> by SimpleItemConverter(
builderFactory = ::UserBuilder,
build = UserBuilder::build,
Expand Down Expand Up @@ -139,6 +165,7 @@ class SchemaGeneratorPluginTest {
assertContains(
schemaContents,
"""
@GeneratedApi
public object UserSchema : ItemSchema.PartitionKey<User, KeyType.Key1<Int>> {
override val converter: UserConverter = UserConverter
override val partitionKey: KeySpec.Key1<Int> = KeySpec.number<Int>("id")
Expand All @@ -149,7 +176,10 @@ class SchemaGeneratorPluginTest {
// GetTable
assertContains(
schemaContents,
"public fun DynamoDbMapper.getUserTable(name: String): Table.PartitionKey<User, KeyType.Key1<Int>> = getTable(name, UserSchema)",
"""
@GeneratedApi
public fun DynamoDbMapper.getUserTable(name: String): Table.PartitionKey<User, KeyType.Key1<Int>> = getTable(name, UserSchema)
""".trimIndent(),
)
}

Expand All @@ -172,6 +202,7 @@ class SchemaGeneratorPluginTest {
assertContains(
schemaContents,
"""
@GeneratedApi
public object BuilderNotRequiredConverter : ItemConverter<BuilderNotRequired> by SimpleItemConverter(
builderFactory = { BuilderNotRequired() },
build = { this },
Expand Down Expand Up @@ -229,12 +260,37 @@ class SchemaGeneratorPluginTest {
val schemaContents = schemaFile.readText()

// Assert a builder is still generated, because we configured GenerateBuilderClasses.ALWAYS
assertContains(schemaContents, "public class BuilderNotRequiredBuilder")
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(): BuilderNotRequired")
assertContains(
schemaContents,
"""
@GeneratedApi
public class BuilderNotRequiredBuilder {
@GeneratedApi
public var id: Int? = null
@GeneratedApi
public var givenName: String? = null
@GeneratedApi
public var surname: String? = null
@GeneratedApi
public var age: Int? = null

@GeneratedApi
public fun build(): BuilderNotRequired {
val id = requireNotNull(id) { "Missing value for id" }
val givenName = requireNotNull(givenName) { "Missing value for givenName" }
val surname = requireNotNull(surname) { "Missing value for surname" }
val age = requireNotNull(age) { "Missing value for age" }

return BuilderNotRequired(
id,
givenName,
surname,
age,
)
}
}
""".trimIndent(),
)
}

@Test
Expand Down Expand Up @@ -382,12 +438,37 @@ class SchemaGeneratorPluginTest {

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")
assertContains(
schemaContents,
"""
@GeneratedApi
public class IgnoredPropertyBuilder {
@GeneratedApi
public var id: Int? = null
@GeneratedApi
public var givenName: String? = null
@GeneratedApi
public var surname: String? = null
@GeneratedApi
public var age: Int? = null

@GeneratedApi
public fun build(): IgnoredProperty {
val id = requireNotNull(id) { "Missing value for id" }
val givenName = requireNotNull(givenName) { "Missing value for givenName" }
val surname = requireNotNull(surname) { "Missing value for surname" }
val age = requireNotNull(age) { "Missing value for age" }

return IgnoredProperty(
id,
givenName,
surname,
age,
)
}
}
""".trimIndent()
)

// ssn is annotated with DynamoDbIgnore
assertFalse(schemaContents.contains("public var ssn: String? = null"))
Expand All @@ -409,6 +490,7 @@ class SchemaGeneratorPluginTest {
assertContains(
schemaContents,
"""
@GeneratedApi
public object CustomUserSchema : ItemSchema.PartitionKey<CustomUser, KeyType.Key1<Int>> {
override val converter: MyCustomUserConverter = MyCustomUserConverter
override val partitionKey: KeySpec.Key1<Int> = KeySpec.number<Int>("id")
Expand Down Expand Up @@ -565,6 +647,7 @@ class SchemaGeneratorPluginTest {
assertContains(
schemaContents,
"""
@GeneratedApi
public object RenamedPartitionKeySchema : ItemSchema.PartitionKey<RenamedPartitionKey, KeyType.Key1<Int>> {
override val converter: RenamedPartitionKeyConverter = RenamedPartitionKeyConverter
override val partitionKey: KeySpec.Key1<Int> = KeySpec.number<Int>("user_id")
Expand Down
Loading
Loading