diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperPkg.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperPkg.kt index 06061951f3b..b93ff887151 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperPkg.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperPkg.kt @@ -20,6 +20,12 @@ public object MapperPkg { public val CollectionValues: String = "$Values.collections" public val ScalarValues: String = "$Values.scalars" public val SmithyTypeValues: String = "$Values.smithytypes" + + @InternalSdkApi + public object Expressions { + public val Base: String = "${Hl.Base}.expressions" + public val Internal: String = "$Base.internal" + } } @InternalSdkApi 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 2b979e5e2ac..364e03cecf0 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 @@ -22,6 +22,18 @@ public object MapperTypes { public val ManualPagination: TypeRef = TypeRef(MapperPkg.Hl.Annotations, "ManualPagination") } + public object Expressions { + public val BooleanExpr: TypeRef = TypeRef(MapperPkg.Hl.Expressions.Base, "BooleanExpr") + public val Filter: TypeRef = TypeRef(MapperPkg.Hl.Expressions.Base, "Filter") + public val KeyFilter: TypeRef = TypeRef(MapperPkg.Hl.Expressions.Base, "KeyFilter") + + public object Internal { + public val FilterImpl: TypeRef = TypeRef(MapperPkg.Hl.Expressions.Internal, "FilterImpl") + public val ParameterizingExpressionVisitor: TypeRef = TypeRef(MapperPkg.Hl.Expressions.Internal, "ParameterizingExpressionVisitor") + public val toExpression: TypeRef = TypeRef(MapperPkg.Hl.Expressions.Internal, "toExpression") + } + } + public object Items { public fun itemSchema(typeVar: String): TypeRef = TypeRef(MapperPkg.Hl.Items, "ItemSchema", genericArgs = listOf(TypeVar(typeVar))) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/ModelAttributes.kt b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/MapperAttributes.kt similarity index 54% rename from hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/ModelAttributes.kt rename to hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/MapperAttributes.kt index 6bbc651fa4e..df7e979b970 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/ModelAttributes.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/MapperAttributes.kt @@ -5,23 +5,12 @@ package aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model import aws.sdk.kotlin.hll.codegen.model.Operation -import aws.sdk.kotlin.hll.codegen.model.Structure import aws.smithy.kotlin.runtime.collections.AttributeKey /** * Defines [AttributeKey] instances that relate to the data model of low-level to high-level codegen */ -internal object ModelAttributes { - /** - * For a given high-level [Operation], this attribute key identifies the associated low-level [Operation] - */ - val LowLevelOperation: AttributeKey = AttributeKey("aws.sdk.kotlin.ddbmapper#LowLevelOperation") - - /** - * For a given high-level [Structure], this attribute key identifies the associated low-level [Structure] - */ - val LowLevelStructure: AttributeKey = AttributeKey("aws.sdk.kotlin.ddbmapper#LowLevelStructure") - +internal object MapperAttributes { /** * For a given [Operation], this attribute key contains relevant pagination members (if applicable) in the request * and response diff --git a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/MemberCodegenBehavior.kt b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/MemberCodegenBehavior.kt index a07c96557ac..6295f09637a 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/MemberCodegenBehavior.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/MemberCodegenBehavior.kt @@ -4,15 +4,12 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model -import aws.sdk.kotlin.hll.codegen.model.Member -import aws.sdk.kotlin.hll.codegen.model.TypeRef -import aws.sdk.kotlin.hll.codegen.model.Types -import aws.sdk.kotlin.hll.codegen.model.nullable +import aws.sdk.kotlin.hll.codegen.model.* import aws.sdk.kotlin.hll.dynamodbmapper.codegen.model.MapperPkg import aws.sdk.kotlin.hll.dynamodbmapper.codegen.model.MapperTypes - -private val attrMapTypes = setOf(MapperTypes.AttributeMap, MapperTypes.AttributeMap.nullable()) -private val attrMapListTypes = Types.Kotlin.list(MapperTypes.AttributeMap).let { setOf(it, it.nullable()) } +import aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model.ExpressionArgumentsType.* +import aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model.ExpressionLiteralType.* +import aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model.MemberCodegenBehavior.* /** * Describes a behavior to apply for a given [Member] in a low-level structure when generating code for an equivalent @@ -60,56 +57,105 @@ internal sealed interface MemberCodegenBehavior { * structure). */ data object Hoist : MemberCodegenBehavior + + /** + * Indicates that a member is a string expression parameter which should be replaced by an expression DSL + * @param type The type of expression this member models + */ + data class ExpressionLiteral(val type: ExpressionLiteralType) : MemberCodegenBehavior + + /** + * Indicates that a member is a map of expression arguments which should be automatically handled by an expression + * DSL + * @param type The type of expression arguments this member models + */ + data class ExpressionArguments(val type: ExpressionArgumentsType) : MemberCodegenBehavior +} + +/** + * Identifies a type of expression literal supported by DynamoDB APIs + */ +internal enum class ExpressionLiteralType { + Condition, + Filter, + KeyCondition, + Projection, + Update, +} + +/** + * Identifies a type of expression arguments supported by DynamoDB APIs + */ +internal enum class ExpressionArgumentsType { + AttributeNames, + AttributeValues, } /** * Identifies a [MemberCodegenBehavior] for this [Member] by way of various heuristics */ internal val Member.codegenBehavior: MemberCodegenBehavior - get() = when { - this in unsupportedMembers -> MemberCodegenBehavior.Drop - type in attrMapTypes -> if (name == "key") MemberCodegenBehavior.MapKeys else MemberCodegenBehavior.MapAll - type in attrMapListTypes -> MemberCodegenBehavior.ListMapAll - isTableName || isIndexName -> MemberCodegenBehavior.Hoist - else -> MemberCodegenBehavior.PassThrough - } + get() = rules.firstNotNullOfOrNull { it.matchedBehaviorOrNull(this) } ?: PassThrough -private val Member.isTableName: Boolean - get() = name == "tableName" && type == Types.Kotlin.StringNullable +private fun llType(name: String) = TypeRef(MapperPkg.Ll.Model, name) -private val Member.isIndexName: Boolean - get() = name == "indexName" && type == Types.Kotlin.StringNullable +private data class Rule( + val namePredicate: (String) -> Boolean, + val typePredicate: (TypeRef) -> Boolean, + val behavior: MemberCodegenBehavior, +) { + constructor(name: String, type: TypeRef, behavior: MemberCodegenBehavior) : + this(name::equals, type::isEquivalentTo, behavior) -private fun llType(name: String) = TypeRef(MapperPkg.Ll.Model, name) + constructor(name: Regex, type: TypeRef, behavior: MemberCodegenBehavior) : + this(name::matches, type::isEquivalentTo, behavior) -private val unsupportedMembers = listOf( - // superseded by ConditionExpression - Member("conditionalOperator", llType("ConditionalOperator")), - Member("expected", Types.Kotlin.stringMap(llType("ExpectedAttributeValue"))), - - // superseded by FilterExpression - Member("queryFilter", Types.Kotlin.stringMap(llType("Condition"))), - Member("scanFilter", Types.Kotlin.stringMap(llType("Condition"))), - - // superseded by KeyConditionExpression - Member("keyConditions", Types.Kotlin.stringMap(llType("Condition"))), - - // superseded by ProjectionExpression - Member("attributesToGet", Types.Kotlin.list(Types.Kotlin.String)), - - // superseded by UpdateExpression - Member("attributeUpdates", Types.Kotlin.stringMap(llType("AttributeValueUpdate"))), - - // TODO add support for expressions - Member("expressionAttributeNames", Types.Kotlin.stringMap(Types.Kotlin.String)), - Member("expressionAttributeValues", MapperTypes.AttributeMap), - Member("conditionExpression", Types.Kotlin.String), - Member("projectionExpression", Types.Kotlin.String), - Member("updateExpression", Types.Kotlin.String), -).map { member -> - if (member.type is TypeRef) { - member.copy(type = member.type.nullable()) - } else { - member - } -}.toSet() + fun matchedBehaviorOrNull(member: Member) = if (matches(member)) behavior else null + fun matches(member: Member) = namePredicate(member.name) && typePredicate(member.type as TypeRef) +} + +private fun Type.isEquivalentTo(other: Type): Boolean = when (this) { + is TypeVar -> other is TypeVar && shortName == other.shortName + is TypeRef -> + other is TypeRef && + fullName == other.fullName && + genericArgs.size == other.genericArgs.size && + genericArgs.zip(other.genericArgs).all { (thisArg, otherArg) -> thisArg.isEquivalentTo(otherArg) } +} + +/** + * Priority-ordered list of dispositions to apply to members found in structures. The first element from this list that + * successfully matches with a member will be chosen. + */ +private val rules = listOf( + // Deprecated expression members not to be carried forward into HLL + Rule("conditionalOperator", llType("ConditionalOperator"), Drop), + Rule("expected", Types.Kotlin.stringMap(llType("ExpectedAttributeValue")), Drop), + Rule("queryFilter", Types.Kotlin.stringMap(llType("Condition")), Drop), + Rule("scanFilter", Types.Kotlin.stringMap(llType("Condition")), Drop), + Rule("keyConditions", Types.Kotlin.stringMap(llType("Condition")), Drop), + Rule("attributesToGet", Types.Kotlin.list(Types.Kotlin.String), Drop), + Rule("attributeUpdates", Types.Kotlin.stringMap(llType("AttributeValueUpdate")), Drop), + + // Hoisted members + Rule("tableName", Types.Kotlin.String, Hoist), + Rule("indexName", Types.Kotlin.String, Hoist), + + // Expression literals + Rule("keyConditionExpression", Types.Kotlin.String, ExpressionLiteral(KeyCondition)), + Rule("filterExpression", Types.Kotlin.String, ExpressionLiteral(Filter)), + + // TODO add support for remaining expression types + Rule("conditionExpression", Types.Kotlin.String, Drop), + Rule("projectionExpression", Types.Kotlin.String, Drop), + Rule("updateExpression", Types.Kotlin.String, Drop), + + // Expression arguments + Rule("expressionAttributeNames", Types.Kotlin.stringMap(Types.Kotlin.String), ExpressionArguments(AttributeNames)), + Rule("expressionAttributeValues", MapperTypes.AttributeMap, ExpressionArguments(AttributeValues)), + + // Mappable members + Rule(".*".toRegex(), Types.Kotlin.list(MapperTypes.AttributeMap), ListMapAll), + Rule("key", MapperTypes.AttributeMap, MapKeys), + Rule(".*".toRegex(), MapperTypes.AttributeMap, MapAll), +) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Operation.kt b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Operation.kt index 84cb030683a..18ec6d05043 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Operation.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Operation.kt @@ -4,15 +4,9 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model +import aws.sdk.kotlin.hll.codegen.model.ModelAttributes import aws.sdk.kotlin.hll.codegen.model.Operation import aws.sdk.kotlin.hll.codegen.util.plus -import aws.smithy.kotlin.runtime.collections.get - -/** - * Gets the low-level [Operation] equivalent for this high-level operation - */ -internal val Operation.lowLevel: Operation - get() = attributes[ModelAttributes.LowLevelOperation] /** * Derives a high-level [Operation] equivalent for this low-level operation diff --git a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Pagination.kt b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Pagination.kt index b521cf68e26..b24bf8f5bf3 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Pagination.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Pagination.kt @@ -39,7 +39,7 @@ internal data class PaginationMembers( * property returns `null`. */ internal val Operation.paginationInfo: PaginationMembers? - get() = attributes.getOrNull(ModelAttributes.PaginationInfo) + get() = attributes.getOrNull(MapperAttributes.PaginationInfo) /** * A codegen plugin that adds DDB-specific pagination info to operations @@ -47,7 +47,7 @@ internal val Operation.paginationInfo: PaginationMembers? internal class DdbPaginationPlugin : ModelParsingPlugin { override fun postProcessOperation(operation: Operation): Operation { val paginationMembers = PaginationMembers.forOperationOrNull(operation) ?: return operation - val newAttributes = operation.attributes + (ModelAttributes.PaginationInfo to paginationMembers) + val newAttributes = operation.attributes + (MapperAttributes.PaginationInfo to paginationMembers) return operation.copy(attributes = newAttributes) } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Structure.kt b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Structure.kt index 4427a5a3424..eed29508721 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Structure.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/model/Structure.kt @@ -6,13 +6,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model import aws.sdk.kotlin.hll.codegen.model.* import aws.sdk.kotlin.hll.codegen.util.plus -import aws.smithy.kotlin.runtime.collections.get - -/** - * Gets the low-level [Structure] equivalent for this high-level structure - */ -internal val Structure.lowLevel: Structure - get() = attributes[ModelAttributes.LowLevelStructure] +import aws.sdk.kotlin.hll.dynamodbmapper.codegen.model.MapperTypes /** * Derives a high-level [Structure] equivalent for this low-level structure @@ -24,20 +18,54 @@ internal fun Structure.toHighLevel(pkg: String): Structure { val hlType = TypeRef(pkg, llStructure.type.shortName, listOf(TypeVar("T"))) val hlMembers = llStructure.members.mapNotNull { llMember -> - when (llMember.codegenBehavior) { - MemberCodegenBehavior.PassThrough -> llMember + val nullable = llMember.type.nullable + + val hlMember = when (val behavior = llMember.codegenBehavior) { + MemberCodegenBehavior.PassThrough, is MemberCodegenBehavior.ExpressionArguments -> llMember MemberCodegenBehavior.MapAll, MemberCodegenBehavior.MapKeys -> - llMember.copy(type = TypeVar("T", llMember.type.nullable)) + llMember.copy(type = TypeVar("T", nullable)) MemberCodegenBehavior.ListMapAll -> { val llListType = llMember.type as? TypeRef ?: error("`ListMapAll` member is required to be a TypeRef") - val hlListType = llListType.copy(genericArgs = listOf(TypeVar("T")), nullable = llListType.nullable) + val hlListType = llListType.copy(genericArgs = listOf(TypeVar("T"))) llMember.copy(type = hlListType) } + is MemberCodegenBehavior.ExpressionLiteral -> { + val expressionType = when (behavior.type) { + ExpressionLiteralType.Filter -> MapperTypes.Expressions.BooleanExpr + ExpressionLiteralType.KeyCondition -> MapperTypes.Expressions.KeyFilter + + // TODO add support for other expression types + else -> return@mapNotNull null + }.nullable(nullable) + + val dslInfo = when (behavior.type) { + ExpressionLiteralType.Filter -> DslInfo( + interfaceType = MapperTypes.Expressions.Filter, + implType = MapperTypes.Expressions.Internal.FilterImpl, + implSingleton = true, + ) + + // KeyCondition doesn't use a top-level DSL (SortKeyCondition is nested) + ExpressionLiteralType.KeyCondition -> null + + // TODO add support for other expression types + else -> return@mapNotNull null + } + + llMember.copy( + name = llMember.name.removeSuffix("Expression"), + type = expressionType, + attributes = llMember.attributes + (ModelAttributes.DslInfo to dslInfo), + ) + } + else -> null } + + hlMember?.copy(attributes = hlMember.attributes + (ModelAttributes.LowLevelMember to llMember)) }.toSet() val hlAttributes = llStructure.attributes + (ModelAttributes.LowLevelStructure to llStructure) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/DataTypeGenerator.kt b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/DataTypeGenerator.kt index 8d2616e1902..c1106bdfd14 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/DataTypeGenerator.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/DataTypeGenerator.kt @@ -6,10 +6,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.rendering import aws.sdk.kotlin.hll.codegen.core.CodeGenerator 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.RenderOptions -import aws.sdk.kotlin.hll.codegen.rendering.Visibility +import aws.sdk.kotlin.hll.codegen.rendering.* import aws.sdk.kotlin.hll.codegen.util.plus /** diff --git a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt index fa34d34a225..9bc4a0d36a9 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-ops-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt @@ -8,6 +8,7 @@ import aws.sdk.kotlin.hll.codegen.core.* import aws.sdk.kotlin.hll.codegen.model.Member import aws.sdk.kotlin.hll.codegen.model.Operation import aws.sdk.kotlin.hll.codegen.model.Structure +import aws.sdk.kotlin.hll.codegen.model.lowLevel import aws.sdk.kotlin.hll.codegen.rendering.RenderContext import aws.sdk.kotlin.hll.codegen.rendering.RendererBase import aws.sdk.kotlin.hll.codegen.rendering.info @@ -24,9 +25,19 @@ internal class OperationRenderer( private val ctx: RenderContext, private val operation: Operation, ) : RendererBase(ctx, operation.name) { - private val members = operation.request.lowLevel.members.groupBy { m -> - m.codegenBehavior.also { ctx.info(" ${m.name} → $it") } - } + private val requestMembers = operation + .request + .also { ctx.info("For type ${it.lowLevelName}:") } + .lowLevel + .members + .groupBy { m -> m.codegenBehavior.also { ctx.info(" ${m.name} → $it") } } + + private val responseMembers = operation + .response + .also { ctx.info("For type ${it.lowLevelName}:") } + .lowLevel + .members + .groupBy { m -> m.codegenBehavior.also { ctx.info(" ${m.name} → $it") } } companion object { fun factoryFunctionName(operation: Operation) = "${operation.methodName}Operation" @@ -61,7 +72,7 @@ internal class OperationRenderer( ) writeInline("serialize = { highLevelReq, schema -> highLevelReq.convert(") - members(MemberCodegenBehavior.Hoist) { + requestMembers(MemberCodegenBehavior.Hoist) { if (name in itemSourceKind.hoistedFields) { writeInline("spec.#L, ", name) } else { @@ -84,29 +95,57 @@ internal class OperationRenderer( imports += ImportDirective(operation.request.lowLevel.type, operation.request.lowLevelName) openBlock("private fun #T.convert(", operation.request.type) - members(MemberCodegenBehavior.Hoist) { write("#L: #T, ", name, type) } - write("schema: #T,", MapperTypes.Items.itemSchema("T")) + requestMembers(MemberCodegenBehavior.Hoist) { write("#L: #T, ", name, type) } + write("schema: #T", MapperTypes.Items.itemSchema("T")) closeAndOpenBlock(") = #L {", operation.request.lowLevelName) - members(MemberCodegenBehavior.PassThrough) { write("#1L = this@convert.#1L", name) } - members(MemberCodegenBehavior.MapKeys) { - write("this@convert.#1L?.let { #1L = schema.converter.convertTo(it, schema.keyAttributeNames) }", name) + requestMembers(MemberCodegenBehavior.PassThrough) { write("#L = this@convert.#L", name, highLevel.name) } + requestMembers(MemberCodegenBehavior.MapKeys) { + write( + "this@convert.#L?.let { #L = schema.converter.convertTo(it, schema.keyAttributeNames) }", + highLevel.name, + name, + ) } - members(MemberCodegenBehavior.MapAll) { - write("this@convert.#1L?.let { #1L = schema.converter.convertTo(it) }", name) + requestMembers(MemberCodegenBehavior.MapAll) { + write("this@convert.#L?.let { #L = schema.converter.convertTo(it) }", highLevel.name, name) } - members(MemberCodegenBehavior.ListMapAll) { - write("#1L = this@convert.#1L?.map { schema.converter.toItem(it) }", name) + requestMembers(MemberCodegenBehavior.ListMapAll) { + write("#L = this@convert.#L?.map { schema.converter.convertTo(it) }", name, highLevel.name) } - members(MemberCodegenBehavior.Hoist) { write("this.#1L = #1L", name) } + requestMembers(MemberCodegenBehavior.Hoist) { write("this.#1L = #1L", name) } + + if (requestMembers.hasExpressions) renderRequestExpressions() + closeBlock("}") } - private fun renderResponse() { - ctx.info("For type ${operation.response.lowLevelName}:") - val members = operation.response.lowLevel.members.groupBy { m -> - m.codegenBehavior.also { ctx.info(" ${m.name} → $it") } + private fun renderRequestExpressions() { + blankLine() + write("val expressionVisitor = #T()", MapperTypes.Expressions.Internal.ParameterizingExpressionVisitor) + + requestMembers(MemberCodegenBehavior.ExpressionLiteral(ExpressionLiteralType.Filter)) { + write("#L = this@convert.#L?.accept(expressionVisitor)", name, highLevel.name) + } + + requestMembers(MemberCodegenBehavior.ExpressionLiteral(ExpressionLiteralType.KeyCondition)) { + write( + "#L = this@convert.#L?.#T(schema)?.accept(expressionVisitor)", + name, + highLevel.name, + MapperTypes.Expressions.Internal.toExpression, + ) + } + + requestMembers(MemberCodegenBehavior.ExpressionArguments(ExpressionArgumentsType.AttributeNames)) { + write("#L = expressionVisitor.expressionAttributeNames()", name) + } + + requestMembers(MemberCodegenBehavior.ExpressionArguments(ExpressionArgumentsType.AttributeValues)) { + write("#L = expressionVisitor.expressionAttributeValues()", name) } + } + private fun renderResponse() { DataTypeGenerator(ctx, this, operation.response).generate() blankLine() @@ -119,19 +158,21 @@ internal class OperationRenderer( MapperTypes.Items.itemSchema("T"), operation.response.type, ) { - members(MemberCodegenBehavior.PassThrough) { write("#1L = this@convert.#1L", name) } + responseMembers(MemberCodegenBehavior.PassThrough) { write("#L = this@convert.#L", highLevel.name, name) } - members(MemberCodegenBehavior.MapKeys, MemberCodegenBehavior.MapAll) { + responseMembers(MemberCodegenBehavior.MapKeys, MemberCodegenBehavior.MapAll) { write( - "#1L = this@convert.#1L?.#2T()?.let(schema.converter::convertFrom)", + "#L = this@convert.#L?.#T()?.let(schema.converter::convertFrom)", + highLevel.name, name, MapperTypes.Model.toItem, ) } - members(MemberCodegenBehavior.ListMapAll) { + responseMembers(MemberCodegenBehavior.ListMapAll) { write( - "#1L = this@convert.#1L?.map { schema.converter.convertFrom(it.#2T()) }", + "#L = this@convert.#L?.map { schema.converter.convertFrom(it.#T()) }", + highLevel.name, name, MapperTypes.Model.toItem, ) @@ -139,13 +180,32 @@ internal class OperationRenderer( } } - private inline operator fun Map>.invoke( - vararg behaviors: MemberCodegenBehavior, - block: Member.() -> Unit, - ) { - behaviors.forEach { behavior -> - get(behavior)?.forEach(block) - } + private val Member.highLevel: Member + get() = + operation.request.members.firstOrNull { it.lowLevel == this } + ?: operation.response.members.first { it.lowLevel == this } +} + +private val Map>.hasExpressions: Boolean + get() = keys.any { + it is MemberCodegenBehavior.ExpressionLiteral || it is MemberCodegenBehavior.ExpressionArguments + } + +private inline operator fun Map>.invoke( + vararg behaviors: MemberCodegenBehavior, + block: Member.() -> Unit, +) { + behaviors.forEach { behavior -> + get(behavior)?.forEach(block) + } +} + +private inline operator fun Map>.invoke( + predicate: (MemberCodegenBehavior) -> Boolean, + block: Member.() -> Unit, +) { + entries.forEach { (behavior, members) -> + if (predicate(behavior)) members.forEach(block) } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api b/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api index 017ba1eb67f..e2af6c44816 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api +++ b/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api @@ -93,7 +93,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributeType : public static fun values ()[Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributeType; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/BetweenExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/BetweenExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr, aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr { public abstract fun accept (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/ExpressionVisitor;)Ljava/lang/Object; public abstract fun getMax ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression; public abstract fun getMin ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression; @@ -123,7 +123,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFunc : j public static fun values ()[Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFunc; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFuncExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFuncExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr, aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr { public abstract fun accept (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/ExpressionVisitor;)Ljava/lang/Object; public abstract fun getAdditionalOperands ()Ljava/util/List; public abstract fun getFunc ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFunc; @@ -153,7 +153,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/Comparator : ja public static fun values ()[Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Comparator; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/ComparisonExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr { +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/ComparisonExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr, aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr { public abstract fun accept (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/ExpressionVisitor;)Ljava/lang/Object; public abstract fun getComparator ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Comparator; public abstract fun getLeft ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression; @@ -231,6 +231,7 @@ public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/Fi public abstract fun gte-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun gte-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun isBetween (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; + public abstract fun isBetween (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;[B[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun isInCollectionByteArray (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/util/Collection;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun isInCollectionExpression (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/util/Collection;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun isInCollectionList (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/util/Collection;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; @@ -296,6 +297,7 @@ public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/Fi public abstract fun or ([Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; + public abstract fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; } public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter$DefaultImpls { @@ -333,6 +335,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter$DefaultI public static fun gte-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public static fun gte-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public static fun gte-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; + public static fun isBetween (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;[B[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public static fun isInCollectionByteArray (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/util/Collection;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public static fun isInCollectionList (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/util/Collection;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public static fun isInCollectionMap (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/util/Collection;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; @@ -389,6 +392,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter$DefaultI public static fun neqSetUShort (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public static fun or (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;[Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public static fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; + public static fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/InExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr { @@ -405,6 +409,16 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/InExprKt { public static final fun InExpr (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression;Ljava/util/Collection;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/InExpr; } +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 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 abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression { public abstract fun accept (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/ExpressionVisitor;)Ljava/lang/Object; public abstract fun getValue ()Laws/sdk/kotlin/services/dynamodb/model/AttributeValue; @@ -487,6 +501,129 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/ScalarFuncExprK public static synthetic fun ScalarFuncExpr$default (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/ScalarFunc;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/AttributePath;Ljava/util/List;ILjava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/ScalarFuncExpr; } +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey { +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr : aws/sdk/kotlin/hll/dynamodbmapper/expressions/Expression { +} + +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter { + public abstract fun eq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun eq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun eq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun eq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun eq-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun eq-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun eq-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun eq-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun getSortKey ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey; + public abstract fun gt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gt-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gt-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gt-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gt-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun gte-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isBetween (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isBetween (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isInRangeNumber (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isInRangeString (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isInRangeUByte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isInRangeUInt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isInRangeULong (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun isInRangeUShort (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lt-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun lte-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun neq-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/LiteralExpr;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public abstract fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; +} + +public final class aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter$DefaultImpls { + public static fun eq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun eq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun eq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun eq-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun eq-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun eq-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun eq-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gt-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gt-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gt-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gt-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gte-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gte-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gte-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun gte-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun isBetween (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun isInRangeNumber (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun isInRangeString (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun isInRangeUByte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun isInRangeUInt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun isInRangeULong (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun isInRangeUShort (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Lkotlin/ranges/ClosedRange;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lt (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lt-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lt-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lt-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lt-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lte (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lte-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lte-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lte-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun lte-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun neq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/Number;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun neq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun neq (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun neq-2TYgG_w (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;J)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun neq-EK-6454 (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun neq-Qn1smSk (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;I)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun neq-i8woANY (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;S)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;Ljava/lang/String;)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; + public static fun startsWith (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter;Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKey;[B)Laws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyExpr; +} + public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/AttributeDescriptor { public abstract fun getConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; public abstract fun getGetter ()Lkotlin/jvm/functions/Function1; @@ -691,6 +828,8 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemKt { public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemRequest { public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemRequest$Companion; + public abstract fun getExpressionAttributeNames ()Ljava/util/Map; + public abstract fun getExpressionAttributeValues ()Ljava/util/Map; public abstract fun getKey ()Ljava/lang/Object; public abstract fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public abstract fun getReturnItemCollectionMetrics ()Laws/sdk/kotlin/services/dynamodb/model/ReturnItemCollectionMetrics; @@ -704,11 +843,15 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemReques public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemRequestBuilder { public fun ()V public final fun build ()Laws/sdk/kotlin/hll/dynamodbmapper/operations/DeleteItemRequest; + public final fun getExpressionAttributeNames ()Ljava/util/Map; + public final fun getExpressionAttributeValues ()Ljava/util/Map; public final fun getKey ()Ljava/lang/Object; public final fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public final fun getReturnItemCollectionMetrics ()Laws/sdk/kotlin/services/dynamodb/model/ReturnItemCollectionMetrics; public final fun getReturnValues ()Laws/sdk/kotlin/services/dynamodb/model/ReturnValue; public final fun getReturnValuesOnConditionCheckFailure ()Laws/sdk/kotlin/services/dynamodb/model/ReturnValuesOnConditionCheckFailure; + public final fun setExpressionAttributeNames (Ljava/util/Map;)V + public final fun setExpressionAttributeValues (Ljava/util/Map;)V public final fun setKey (Ljava/lang/Object;)V public final fun setReturnConsumedCapacity (Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity;)V public final fun setReturnItemCollectionMetrics (Laws/sdk/kotlin/services/dynamodb/model/ReturnItemCollectionMetrics;)V @@ -749,6 +892,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemKt { public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemRequest { public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemRequest$Companion; public abstract fun getConsistentRead ()Ljava/lang/Boolean; + public abstract fun getExpressionAttributeNames ()Ljava/util/Map; public abstract fun getKey ()Ljava/lang/Object; public abstract fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; } @@ -760,9 +904,11 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemRequestBu public fun ()V public final fun build ()Laws/sdk/kotlin/hll/dynamodbmapper/operations/GetItemRequest; public final fun getConsistentRead ()Ljava/lang/Boolean; + public final fun getExpressionAttributeNames ()Ljava/util/Map; public final fun getKey ()Ljava/lang/Object; public final fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public final fun setConsistentRead (Ljava/lang/Boolean;)V + public final fun setExpressionAttributeNames (Ljava/util/Map;)V public final fun setKey (Ljava/lang/Object;)V public final fun setReturnConsumedCapacity (Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity;)V } @@ -824,6 +970,8 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemKt { public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemRequest { public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemRequest$Companion; + public abstract fun getExpressionAttributeNames ()Ljava/util/Map; + public abstract fun getExpressionAttributeValues ()Ljava/util/Map; public abstract fun getItem ()Ljava/lang/Object; public abstract fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public abstract fun getReturnItemCollectionMetrics ()Laws/sdk/kotlin/services/dynamodb/model/ReturnItemCollectionMetrics; @@ -837,11 +985,15 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemRequest$C public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemRequestBuilder { public fun ()V public final fun build ()Laws/sdk/kotlin/hll/dynamodbmapper/operations/PutItemRequest; + public final fun getExpressionAttributeNames ()Ljava/util/Map; + public final fun getExpressionAttributeValues ()Ljava/util/Map; public final fun getItem ()Ljava/lang/Object; public final fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public final fun getReturnItemCollectionMetrics ()Laws/sdk/kotlin/services/dynamodb/model/ReturnItemCollectionMetrics; public final fun getReturnValues ()Laws/sdk/kotlin/services/dynamodb/model/ReturnValue; public final fun getReturnValuesOnConditionCheckFailure ()Laws/sdk/kotlin/services/dynamodb/model/ReturnValuesOnConditionCheckFailure; + public final fun setExpressionAttributeNames (Ljava/util/Map;)V + public final fun setExpressionAttributeValues (Ljava/util/Map;)V public final fun setItem (Ljava/lang/Object;)V public final fun setReturnConsumedCapacity (Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity;)V public final fun setReturnItemCollectionMetrics (Laws/sdk/kotlin/services/dynamodb/model/ReturnItemCollectionMetrics;)V @@ -883,8 +1035,10 @@ public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/operations/Que public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/operations/QueryRequest$Companion; public abstract fun getConsistentRead ()Ljava/lang/Boolean; public abstract fun getExclusiveStartKey ()Ljava/lang/Object; - public abstract fun getFilterExpression ()Ljava/lang/String; - public abstract fun getKeyConditionExpression ()Ljava/lang/String; + public abstract fun getExpressionAttributeNames ()Ljava/util/Map; + public abstract fun getExpressionAttributeValues ()Ljava/util/Map; + public abstract fun getFilter ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; + public abstract fun getKeyCondition ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; public abstract fun getLimit ()Ljava/lang/Integer; public abstract fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public abstract fun getScanIndexForward ()Ljava/lang/Boolean; @@ -897,18 +1051,23 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryRequest$Com public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryRequestBuilder { public fun ()V public final fun build ()Laws/sdk/kotlin/hll/dynamodbmapper/operations/QueryRequest; + public final fun filter (Lkotlin/jvm/functions/Function1;)V public final fun getConsistentRead ()Ljava/lang/Boolean; public final fun getExclusiveStartKey ()Ljava/lang/Object; - public final fun getFilterExpression ()Ljava/lang/String; - public final fun getKeyConditionExpression ()Ljava/lang/String; + public final fun getExpressionAttributeNames ()Ljava/util/Map; + public final fun getExpressionAttributeValues ()Ljava/util/Map; + public final fun getFilter ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; + public final fun getKeyCondition ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter; public final fun getLimit ()Ljava/lang/Integer; public final fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public final fun getScanIndexForward ()Ljava/lang/Boolean; public final fun getSelect ()Laws/sdk/kotlin/services/dynamodb/model/Select; public final fun setConsistentRead (Ljava/lang/Boolean;)V public final fun setExclusiveStartKey (Ljava/lang/Object;)V - public final fun setFilterExpression (Ljava/lang/String;)V - public final fun setKeyConditionExpression (Ljava/lang/String;)V + public final fun setExpressionAttributeNames (Ljava/util/Map;)V + public final fun setExpressionAttributeValues (Ljava/util/Map;)V + public final fun setFilter (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr;)V + public final fun setKeyCondition (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter;)V public final fun setLimit (Ljava/lang/Integer;)V public final fun setReturnConsumedCapacity (Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity;)V public final fun setScanIndexForward (Ljava/lang/Boolean;)V @@ -955,7 +1114,9 @@ public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/operations/Sca public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/operations/ScanRequest$Companion; public abstract fun getConsistentRead ()Ljava/lang/Boolean; public abstract fun getExclusiveStartKey ()Ljava/lang/Object; - public abstract fun getFilterExpression ()Ljava/lang/String; + public abstract fun getExpressionAttributeNames ()Ljava/util/Map; + public abstract fun getExpressionAttributeValues ()Ljava/util/Map; + public abstract fun getFilter ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public abstract fun getLimit ()Ljava/lang/Integer; public abstract fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public abstract fun getSegment ()Ljava/lang/Integer; @@ -969,9 +1130,12 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanRequest$Comp public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanRequestBuilder { public fun ()V public final fun build ()Laws/sdk/kotlin/hll/dynamodbmapper/operations/ScanRequest; + public final fun filter (Lkotlin/jvm/functions/Function1;)V public final fun getConsistentRead ()Ljava/lang/Boolean; public final fun getExclusiveStartKey ()Ljava/lang/Object; - public final fun getFilterExpression ()Ljava/lang/String; + public final fun getExpressionAttributeNames ()Ljava/util/Map; + public final fun getExpressionAttributeValues ()Ljava/util/Map; + public final fun getFilter ()Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr; public final fun getLimit ()Ljava/lang/Integer; public final fun getReturnConsumedCapacity ()Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity; public final fun getSegment ()Ljava/lang/Integer; @@ -979,7 +1143,9 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanRequestBuild public final fun getTotalSegments ()Ljava/lang/Integer; public final fun setConsistentRead (Ljava/lang/Boolean;)V public final fun setExclusiveStartKey (Ljava/lang/Object;)V - public final fun setFilterExpression (Ljava/lang/String;)V + public final fun setExpressionAttributeNames (Ljava/util/Map;)V + public final fun setExpressionAttributeValues (Ljava/util/Map;)V + public final fun setFilter (Laws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanExpr;)V public final fun setLimit (Ljava/lang/Integer;)V public final fun setReturnConsumedCapacity (Laws/sdk/kotlin/services/dynamodb/model/ReturnConsumedCapacity;)V public final fun setSegment (Ljava/lang/Integer;)V diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BetweenExpr.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BetweenExpr.kt index bbed7012cbb..676937cf780 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BetweenExpr.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BetweenExpr.kt @@ -11,7 +11,9 @@ import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.BetweenExprImpl * [DynamoDB's **making comparisons** documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Comparators). * This expression will be true if `value >= min && value <= max`. */ -public interface BetweenExpr : BooleanExpr { +public interface BetweenExpr : + BooleanExpr, + SortKeyExpr { /** * The value being compared to the [min] and [max] */ diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFuncExpr.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFuncExpr.kt index c110c7200f8..8d466256cda 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFuncExpr.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/BooleanFuncExpr.kt @@ -10,7 +10,9 @@ import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.BooleanFuncExprImp * Represents a function expression that yields a boolean result as described in * [DynamoDB's **function** documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) */ -public interface BooleanFuncExpr : BooleanExpr { +public interface BooleanFuncExpr : + BooleanExpr, + SortKeyExpr { /** * The specific boolean function to use */ diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/ComparisonExpr.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/ComparisonExpr.kt index 7a1ab717bc8..c6d4772db80 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/ComparisonExpr.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/ComparisonExpr.kt @@ -11,7 +11,9 @@ import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.ComparisonExprImpl * [DynamoDB's **making comparisons** documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Comparators). * The specific type of comparison is identified by the [comparator] field. */ -public interface ComparisonExpr : BooleanExpr { +public interface ComparisonExpr : + BooleanExpr, + SortKeyExpr { /** * The [Comparator] to use in the expression */ diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Expressions.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Expressions.kt index 06e0bf0f8e2..a5c7000c319 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Expressions.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Expressions.kt @@ -27,4 +27,6 @@ public sealed interface Expression { * items. This is a [marker interface](https://en.wikipedia.org/wiki/Marker_interface_pattern) which adds no additional * declarations. */ -public interface BooleanExpr : Expression +public sealed interface BooleanExpr : Expression + +public sealed interface SortKeyExpr : Expression diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter.kt index f49162422f8..f0e4a8fe4fe 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/Filter.kt @@ -660,6 +660,14 @@ public interface Filter { */ public fun AttributePath.isBetween(min: Expression, max: Expression): BooleanExpr + /** + * Creates a range expression for verifying the sort key is between two other expressions + * @param min The lower bound value + * @param max The upper bound value (inclusive) + */ + public fun AttributePath.isBetween(min: ByteArray, max: ByteArray): BooleanExpr = + isBetween(LiteralExpr(min), LiteralExpr(max)) + // TODO The following overloads support [ClosedRange] but [OpenEndRange] also exists. DynamoDB expressions don't // support it directly but we may be able to cheese it with two inequalities ANDed together. @@ -894,6 +902,12 @@ public interface Filter { */ public infix fun AttributePath.startsWith(expr: Expression): BooleanExpr + /** + * Creates an expression for verifying this attribute starts with the given expression + * @param value The value to test for at the beginning of this attribute + */ + public infix fun AttributePath.startsWith(value: ByteArray): BooleanExpr = startsWith(LiteralExpr(value)) + /** * Creates an expression for verifying this attribute starts with the given expression * @param value The value to test for at the beginning of this attribute 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 new file mode 100644 index 00000000000..ad10c72d76c --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilter.kt @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +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 + +/** + * Represents a filter which limits a Query operation to a specific partition key and optional sort key criteria (if + * applicable) + */ +public interface KeyFilter { + /** + * The required value of the partition key + */ + public val partitionKey: Any + + /** + * The sort key expression (if set) + */ + public val sortKey: SortKeyExpr? +} + +/** + * 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). + */ +public fun KeyFilter(partitionKey: Any): KeyFilter = KeyFilterImpl(partitionKey, null) + +/** + * 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. + */ +public fun KeyFilter(partitionKey: Any, sortKey: SortKeyFilter.() -> SortKeyExpr): KeyFilter = + KeyFilterImpl(partitionKey, SortKeyFilterImpl.run(sortKey)) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter.kt new file mode 100644 index 00000000000..46da6723e58 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilter.kt @@ -0,0 +1,449 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.expressions + +/** + * Represents a sort key independent of schema + */ +public interface SortKey + +/** + * A DSL interface providing support for "low-level" sort key filter expressions. Implementations of this interface + * provide methods and properties which create sort key expressions to narrow results in Query operations. Expressions + * are formed by referencing [sortKey] and then exercising some function upon it. + * + * For example: + * + * ```kotlin + * { sortKey eq 42 } + * ``` + * + * This example creates an expression which checks whether an attribute named `foo` is equal to the value `42`. + * + * ## (Non-)Relationship to schema + * + * The expressions formed by [SortKeyFilter] are referred to as a "low-level" filter expression. This is because they + * are not restricted by or adherent to any defined schema. Instead, they are a DSL convenience layer over [literal + * DynamoDB expression strings and expression attribute value maps](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.KeyConditionExpressions.html). + * As such they provide **minimal type correctness** and may allow you to form expressions which are invalid given the + * shape of your data, such with mismatched data types. + * + * # Equalities/inequalities + * + * A very common filter condition is verifying whether the value of the sort key is equal (or unequal) to another a + * literal value. These comparisons are available by using the following functions: + * * [eq] — checks if two values are equal (equivalent to Kotlin's `==` operator) + * * [neq] — checks if two values are _not_ equal (equivalent to Kotlin's `!=` operator) + * * [lt] — checks if a value is less than another value (equivalent to Kotlin's `<` operator) + * * [lte] — checks if a value is less than _or equal to_ another value (equivalent to Kotlin's `<=` operator) + * * [gt] — checks if a value is greater than another value (equivalent to Kotlin's `>` operator) + * * [gte] — checks if a value is greater than _or equal to_ another value (equivalent to Kotlin's `>=` operator) + * + * For example: + * + * ```kotlin + * sortKey eq 5 // Checks whether the value of the sort key is `5` + * sortKey gt "baz" // Checks whether the value of the sort key is greater than `"baz"` + * ``` + * + * # Ranges and sets + * + * Expressions can check whether the value of the sort key is in a given range possible values. These checks are + * available via the [isBetween] or [isIn] functions: + * + * ```kotlin + * // Checks whether the value of the sort key is between 40 and 60 (inclusive) + * sortKey isIn 40..60 + * ``` + * + * # Prefixes + * + * The [startsWith] function enables expressing a prefix for the value of the sort key. For example: + * + * ```kotlin + * sortKey startsWith "abc" // Checks whether the value of the sort key starts with `"abc"` + * ``` + */ +public interface SortKeyFilter { + /** + * Gets an attribute reference to the sort key + */ + public val sortKey: SortKey + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param expr The other expression in the comparison + */ + public infix fun SortKey.eq(expr: LiteralExpr): SortKeyExpr + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.eq(value: ByteArray): SortKeyExpr = eq(LiteralExpr(value)) + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.eq(value: Number): SortKeyExpr = eq(LiteralExpr(value)) + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.eq(value: String): SortKeyExpr = eq(LiteralExpr(value)) + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.eq(value: UByte): SortKeyExpr = eq(LiteralExpr(value)) + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.eq(value: UInt): SortKeyExpr = eq(LiteralExpr(value)) + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.eq(value: ULong): SortKeyExpr = eq(LiteralExpr(value)) + + /** + * Creates an equality expression for verifying the sort key is equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.eq(value: UShort): SortKeyExpr = eq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param expr The other expression in the comparison + */ + public infix fun SortKey.neq(expr: LiteralExpr): SortKeyExpr + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.neq(value: ByteArray): SortKeyExpr = neq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.neq(value: Number): SortKeyExpr = neq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.neq(value: String): SortKeyExpr = neq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.neq(value: UByte): SortKeyExpr = neq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.neq(value: UInt): SortKeyExpr = neq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.neq(value: ULong): SortKeyExpr = neq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is not equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.neq(value: UShort): SortKeyExpr = neq(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param expr The other expression in the comparison + */ + public infix fun SortKey.lt(expr: LiteralExpr): SortKeyExpr + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lt(value: ByteArray): SortKeyExpr = lt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lt(value: Number): SortKeyExpr = lt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lt(value: String): SortKeyExpr = lt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lt(value: UByte): SortKeyExpr = lt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lt(value: UInt): SortKeyExpr = lt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lt(value: ULong): SortKeyExpr = lt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lt(value: UShort): SortKeyExpr = lt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param expr The other expression in the comparison + */ + public infix fun SortKey.lte(expr: LiteralExpr): SortKeyExpr + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param expr The other expression in the comparison + */ + public infix fun SortKey.lte(value: ByteArray): SortKeyExpr = lte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lte(value: Number): SortKeyExpr = lte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lte(value: String): SortKeyExpr = lte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lte(value: UByte): SortKeyExpr = lte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lte(value: UInt): SortKeyExpr = lte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lte(value: ULong): SortKeyExpr = lte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is less than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.lte(value: UShort): SortKeyExpr = lte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param expr The other expression in the comparison + */ + public infix fun SortKey.gt(expr: LiteralExpr): SortKeyExpr + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gt(value: ByteArray): SortKeyExpr = gt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gt(value: Number): SortKeyExpr = gt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gt(value: String): SortKeyExpr = gt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gt(value: UByte): SortKeyExpr = gt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gt(value: UInt): SortKeyExpr = gt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gt(value: ULong): SortKeyExpr = gt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gt(value: UShort): SortKeyExpr = gt(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param expr The other expression in the comparison + */ + public infix fun SortKey.gte(expr: LiteralExpr): SortKeyExpr + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gte(value: ByteArray): SortKeyExpr = gte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gte(value: Number): SortKeyExpr = gte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gte(value: String): SortKeyExpr = gte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gte(value: UByte): SortKeyExpr = gte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gte(value: UInt): SortKeyExpr = gte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gte(value: ULong): SortKeyExpr = gte(LiteralExpr(value)) + + /** + * Creates an inequality expression for verifying the sort key is greater than or equal to another expression + * @param value The other value in the comparison + */ + public infix fun SortKey.gte(value: UShort): SortKeyExpr = gte(LiteralExpr(value)) + + /** + * Creates a range expression for verifying the sort key is between two other expressions + * @param min The lower bound expression + * @param max The upper bound expression (inclusive) + */ + public fun SortKey.isBetween(min: LiteralExpr, max: LiteralExpr): SortKeyExpr + + /** + * Creates a range expression for verifying the sort key is between two other expressions + * @param min The lower bound value + * @param max The upper bound value (inclusive) + */ + public fun SortKey.isBetween(min: ByteArray, max: ByteArray): SortKeyExpr = + isBetween(LiteralExpr(min), LiteralExpr(max)) + + /** + * Creates a range expression for verifying the sort key is in the given range + * @param range The range to check + */ + @Suppress("INAPPLICABLE_JVM_NAME") + @JvmName("isInRangeNumber") + public infix fun SortKey.isIn(range: ClosedRange): SortKeyExpr where N : Number, N : Comparable = + isBetween(LiteralExpr(range.start), LiteralExpr(range.endInclusive)) + + /** + * Creates a range expression for verifying the sort key is in the given range + * @param range The range to check + */ + @Suppress("INAPPLICABLE_JVM_NAME") + @JvmName("isInRangeString") + public infix fun SortKey.isIn(range: ClosedRange): SortKeyExpr = + isBetween(LiteralExpr(range.start), LiteralExpr(range.endInclusive)) + + /** + * Creates a range expression for verifying the sort key is in the given range + * @param range The range to check + */ + @Suppress("INAPPLICABLE_JVM_NAME") + @JvmName("isInRangeUByte") + public infix fun SortKey.isIn(range: ClosedRange): SortKeyExpr = + isBetween(LiteralExpr(range.start), LiteralExpr(range.endInclusive)) + + /** + * Creates a range expression for verifying the sort key is in the given range + * @param range The range to check + */ + @Suppress("INAPPLICABLE_JVM_NAME") + @JvmName("isInRangeUInt") + public infix fun SortKey.isIn(range: ClosedRange): SortKeyExpr = + isBetween(LiteralExpr(range.start), LiteralExpr(range.endInclusive)) + + /** + * Creates a range expression for verifying the sort key is in the given range + * @param range The range to check + */ + @Suppress("INAPPLICABLE_JVM_NAME") + @JvmName("isInRangeULong") + public infix fun SortKey.isIn(range: ClosedRange): SortKeyExpr = + isBetween(LiteralExpr(range.start), LiteralExpr(range.endInclusive)) + + /** + * Creates a range expression for verifying the sort key is in the given range + * @param range The range to check + */ + @Suppress("INAPPLICABLE_JVM_NAME") + @JvmName("isInRangeUShort") + public infix fun SortKey.isIn(range: ClosedRange): SortKeyExpr = + isBetween(LiteralExpr(range.start), LiteralExpr(range.endInclusive)) + + /** + * Creates an expression for verifying the sort key starts with the given expression + * @param expr The expression to test for at the beginning of this attribute + */ + public infix fun SortKey.startsWith(expr: LiteralExpr): SortKeyExpr + + /** + * Creates an expression for verifying the sort key starts with the given expression + * @param value The value to test for at the beginning of this attribute + */ + public infix fun SortKey.startsWith(value: ByteArray): SortKeyExpr = startsWith(LiteralExpr(value)) + + /** + * Creates an expression for verifying the sort key starts with the given expression + * @param value The value to test for at the beginning of this attribute + */ + public infix fun SortKey.startsWith(value: String): SortKeyExpr = startsWith(LiteralExpr(value)) +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/AttributePathImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/AttributePathImpl.kt index c1cbbec9643..fda3ec91ebb 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/AttributePathImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/AttributePathImpl.kt @@ -21,3 +21,12 @@ internal data class AttributePathImpl( } } } + +/** + * An abstract attribute path that represents the sort key in a given schema. This isn't a concrete path and will be + * replaced by the schema's _actual_ sort key in [KeyFilterImpl.toExpression]. + */ +internal data object SkAttrPathImpl : AttributePath { + override val element get() = error("Unsupported") + override val parent get() = error("Unsupported") +} 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 new file mode 100644 index 00000000000..2b83b61c5c4 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/KeyFilterImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +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.util.dynamicAttr +import aws.sdk.kotlin.hll.dynamodbmapper.util.requireNull + +internal data class KeyFilterImpl(override val partitionKey: Any, override val sortKey: SortKeyExpr?) : KeyFilter + +internal fun KeyFilter.toExpression(schema: ItemSchema<*>) = when (schema) { + is ItemSchema.CompositeKey<*, *, *> -> { + val pkCondition = pkCondition(schema, partitionKey) + + 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) + } + + and(pkCondition, skCondition) + } + } ?: pkCondition + } + + is ItemSchema.PartitionKey<*, *> -> { + requireNull(sortKey) { "Sort key condition not allowed on schema without a sort key" } + pkCondition(schema, partitionKey) + } + + else -> error("Unknown schema type ${schema::class} (expected ItemSchema.CompositeKey or ItemSchema.PartitionKey)") +} + +private fun pkCondition(schema: ItemSchema.PartitionKey<*, *>, partitionKey: Any) = + FilterImpl.run { attr(schema.partitionKey.name) eq LiteralExpr(dynamicAttr(partitionKey)) } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/ParameterizingExpressionVisitor.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/ParameterizingExpressionVisitor.kt index f28e6d1df61..f04af75a1c4 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/ParameterizingExpressionVisitor.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/ParameterizingExpressionVisitor.kt @@ -26,7 +26,7 @@ import aws.sdk.kotlin.services.dynamodb.model.AttributeValue * } * ``` */ -internal class ParameterizingExpressionVisitor : ExpressionVisitor { +internal open class ParameterizingExpressionVisitor : ExpressionVisitor { private val namePlaceholders = mutableMapOf() private val valuePlaceholders = mutableMapOf() diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/SortKeyFilterImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/SortKeyFilterImpl.kt new file mode 100644 index 00000000000..944bbfb5c6b --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/expressions/internal/SortKeyFilterImpl.kt @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal + +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.* + +private data object SortKeyImpl : SortKey + +internal data object SortKeyFilterImpl : SortKeyFilter { + override val sortKey: SortKey + get() = SortKeyImpl + + override infix fun SortKey.eq(expr: LiteralExpr) = + ComparisonExpr(Comparator.EQUALS, SkAttrPathImpl, expr) + + override infix fun SortKey.neq(expr: LiteralExpr) = + ComparisonExpr(Comparator.NOT_EQUALS, SkAttrPathImpl, expr) + + override infix fun SortKey.lt(expr: LiteralExpr) = + ComparisonExpr(Comparator.LESS_THAN, SkAttrPathImpl, expr) + + override infix fun SortKey.lte(expr: LiteralExpr) = + ComparisonExpr(Comparator.LESS_THAN_OR_EQUAL, SkAttrPathImpl, expr) + + override infix fun SortKey.gt(expr: LiteralExpr) = + ComparisonExpr(Comparator.GREATER_THAN, SkAttrPathImpl, expr) + + override infix fun SortKey.gte(expr: LiteralExpr) = + ComparisonExpr(Comparator.GREATER_THAN_OR_EQUAL, SkAttrPathImpl, expr) + + override fun SortKey.isBetween(min: LiteralExpr, max: LiteralExpr) = + BetweenExpr(SkAttrPathImpl, min, max) + + override infix fun SortKey.startsWith(expr: LiteralExpr) = + BooleanFuncExpr(BooleanFunc.BEGINS_WITH, SkAttrPathImpl, expr) +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/ContextUtils.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/ContextUtils.kt index 0848c7f5bc4..71287e2ad8c 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/ContextUtils.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/ContextUtils.kt @@ -4,9 +4,6 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.pipeline.internal -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract - internal interface Combinable { operator fun plus(value: V): T } @@ -17,17 +14,3 @@ internal interface ErrorCombinable { } internal fun Throwable?.suppressing(e: Throwable?) = this?.apply { e?.let(::addSuppressed) } ?: e - -@OptIn(ExperimentalContracts::class) -internal fun requireNull(value: T?, lazyMessage: () -> Any): T? { - contract { - returns() implies (value == null) - } - - if (value == null) { - return null - } else { - val message = lazyMessage() - throw IllegalArgumentException(message.toString()) - } -} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/HResContextImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/HResContextImpl.kt index 596cb1b219f..abc8aa14d55 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/HResContextImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/HResContextImpl.kt @@ -7,6 +7,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.pipeline.internal import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.HResContext import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.MapperContext +import aws.sdk.kotlin.hll.dynamodbmapper.util.requireNull internal data class HResContextImpl( override val highLevelRequest: HReq, diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LReqContextImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LReqContextImpl.kt index 813650a32ee..a22a76bd6de 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LReqContextImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LReqContextImpl.kt @@ -7,6 +7,7 @@ package aws.sdk.kotlin.hll.dynamodbmapper.pipeline.internal import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.LReqContext import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.MapperContext +import aws.sdk.kotlin.hll.dynamodbmapper.util.requireNull internal data class LReqContextImpl( override val highLevelRequest: HReq, diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LResContextImpl.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LResContextImpl.kt index 8b71cf6540a..7e299057d45 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LResContextImpl.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/LResContextImpl.kt @@ -8,6 +8,7 @@ import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.DeserializeInput import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.LResContext import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.MapperContext +import aws.sdk.kotlin.hll.dynamodbmapper.util.requireNull internal data class LResContextImpl( override val highLevelRequest: HReq, diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/util/Conditions.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/util/Conditions.kt new file mode 100644 index 00000000000..92970c22a16 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/util/Conditions.kt @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.util + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +@OptIn(ExperimentalContracts::class) +internal fun requireNull(value: T?, lazyMessage: () -> Any): T? { + contract { + returns() implies (value == null) + } + + if (value == null) { + return null + } else { + val message = lazyMessage() + throw IllegalArgumentException(message.toString()) + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/FilterTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/FilterTest.kt index 72f00391213..a075057d3fc 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/FilterTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/FilterTest.kt @@ -6,6 +6,8 @@ package aws.sdk.kotlin.hll.dynamodbmapper.expressions import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.FilterImpl import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.ParameterizingExpressionVisitor +import aws.sdk.kotlin.hll.dynamodbmapper.testutils.UByteRange +import aws.sdk.kotlin.hll.dynamodbmapper.testutils.UShortRange import aws.sdk.kotlin.hll.dynamodbmapper.util.attr import aws.sdk.kotlin.services.dynamodb.model.AttributeValue import kotlin.test.Test @@ -44,11 +46,11 @@ class FilterTest { @Test fun testByteArrays() { - listOf( - byteArrayOf(1, 2, 3), - byteArrayOf(4, 5, 6), - byteArrayOf(7, 8, 9), - ).forEach { value -> + val b1 = byteArrayOf(1, 2, 3) + val b2 = byteArrayOf(4, 5, 6) + val b3 = byteArrayOf(7, 8, 9) + + listOf(b1, b2, b3).forEach { value -> testFilters( attr(value), "foo = :v0" to { attr("foo") eq value }, @@ -61,6 +63,14 @@ class FilterTest { ) } + testFilters( + mapOf( + ":v0" to attr(b1), + ":v1" to attr(b2), + ), + "foo BETWEEN :v0 AND :v1" to { attr("foo").isBetween(b1, b2) }, + ) + (null as ByteArray?).let { value -> testFilters( attr(value), @@ -792,8 +802,3 @@ class FilterTest { assertEquals(expectedANs, actualANs) } } - -// Weirdly, Kotlin stdlib doesn't have range implementations for UByte and UShort (but it _does_ have UInt and ULong). -// So we're rolling our own here! -private data class UByteRange(override val start: UByte, override val endInclusive: UByte) : ClosedRange -private data class UShortRange(override val start: UShort, override val endInclusive: UShort) : ClosedRange 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 new file mode 100644 index 00000000000..09b4f1387b4 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/KeyFilterTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.expressions + +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.FilterImpl +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.toExpression +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.model.Item +import org.junit.jupiter.api.assertThrows +import kotlin.test.Test +import kotlin.test.assertEquals + +class KeyFilterTest { + private val singleKeySchema = ItemSchema(DummyConverter, KeySpec.String("primary")) + private val compositeSchema = ItemSchema(DummyConverter, KeySpec.String("primary"), KeySpec.Number("secondary")) + + @Test + fun testSingleKeySchema() { + val kf = KeyFilter("foo") + val actual = kf.toExpression(singleKeySchema) + val expected = FilterImpl.run { attr("primary") eq "foo" } + + assertEquals(expected, actual) + } + + @Test + fun testSingleKeySchemaWithErroneousSortKey() { + val kf = KeyFilter("foo") { sortKey eq 2 } + + assertThrows { + kf.toExpression(singleKeySchema) + } + } + + @Test + fun testCompositeSchema() { + val kf = KeyFilter("foo") { sortKey lte 10 } + val actual = kf.toExpression(compositeSchema) + val expected = FilterImpl.run { + and( + attr("primary") eq "foo", + attr("secondary") lte 10, + ) + } + + assertEquals(expected, actual) + } + + @Test + fun testCompositeSchemaWithoutSortKey() { + val kf = KeyFilter("foo") + val actual = kf.toExpression(compositeSchema) + val expected = FilterImpl.run { attr("primary") eq "foo" } + + assertEquals(expected, actual) + } +} + +object DummyConverter : ItemConverter { + override fun convertFrom(to: Item) = error("Not needed") + override fun convertTo(from: Any, onlyAttributes: Set?) = error("Not needed") +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilterTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilterTest.kt new file mode 100644 index 00000000000..1f29926a044 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/expressions/SortKeyFilterTest.kt @@ -0,0 +1,239 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.expressions + +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.ParameterizingExpressionVisitor +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.SkAttrPathImpl +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.internal.SortKeyFilterImpl +import aws.sdk.kotlin.hll.dynamodbmapper.testutils.UByteRange +import aws.sdk.kotlin.hll.dynamodbmapper.testutils.UShortRange +import aws.sdk.kotlin.hll.dynamodbmapper.util.attr +import aws.sdk.kotlin.services.dynamodb.model.AttributeValue +import kotlin.test.Test +import kotlin.test.assertEquals + +class SortKeyFilterTest { + @Test + fun testByteArrays() { + val b1 = byteArrayOf(1, 2, 3) + val b2 = byteArrayOf(4, 5, 6) + val b3 = byteArrayOf(7, 8, 9) + + listOf(b1, b2, b3).forEach { value -> + testFilters( + attr(value), + "foo = :v0" to { sortKey eq value }, + "foo <> :v0" to { sortKey neq value }, + "foo < :v0" to { sortKey lt value }, + "foo <= :v0" to { sortKey lte value }, + "foo > :v0" to { sortKey gt value }, + "foo >= :v0" to { sortKey gte value }, + "begins_with(foo, :v0)" to { sortKey startsWith value }, + ) + } + + testFilters( + mapOf( + ":v0" to attr(b1), + ":v1" to attr(b2), + ), + "foo BETWEEN :v0 AND :v1" to { sortKey.isBetween(b1, b2) }, + ) + } + + @Test + fun testNumbers() { + listOf( + 13.toByte(), + (-42).toShort(), + -5, + 31_556_952_000L, + 2.71828f, + 3.14159, + ).forEach { value -> + testFilters( + attr(value), + "foo = :v0" to { sortKey eq value }, + "foo <> :v0" to { sortKey neq value }, + "foo < :v0" to { sortKey lt value }, + "foo <= :v0" to { sortKey lte value }, + "foo > :v0" to { sortKey gt value }, + "foo >= :v0" to { sortKey gte value }, + ) + } + + testFilters( + mapOf( + ":v0" to attr(100), + ":v1" to attr(200), + ), + "foo BETWEEN :v0 AND :v1" to { sortKey isIn 100..200 }, + ) + } + + @Test + fun testStrings() { + listOf( + "apple", + "banana", + "cherry", + ).forEach { value -> + testFilters( + attr(value), + "foo = :v0" to { sortKey eq value }, + "foo <> :v0" to { sortKey neq value }, + "foo < :v0" to { sortKey lt value }, + "foo <= :v0" to { sortKey lte value }, + "foo > :v0" to { sortKey gt value }, + "foo >= :v0" to { sortKey gte value }, + "begins_with(foo, :v0)" to { sortKey startsWith value }, + ) + } + + testFilters( + mapOf( + ":v0" to attr("apple"), + ":v1" to attr("banana"), + ), + "foo BETWEEN :v0 AND :v1" to { sortKey isIn "apple".."banana" }, + ) + } + + @Test + fun testUBytes() { + listOf( + UByte.MIN_VALUE, + 42.toUByte(), + UByte.MAX_VALUE, + ).forEach { value -> + testFilters( + attr(value), + "foo = :v0" to { sortKey eq value }, + "foo <> :v0" to { sortKey neq value }, + "foo < :v0" to { sortKey lt value }, + "foo <= :v0" to { sortKey lte value }, + "foo > :v0" to { sortKey gt value }, + "foo >= :v0" to { sortKey gte value }, + ) + } + + testFilters( + mapOf( + ":v0" to attr(100.toUByte()), + ":v1" to attr(200.toUByte()), + ), + "foo BETWEEN :v0 AND :v1" to { sortKey isIn UByteRange(100.toUByte(), 200.toUByte()) }, + ) + } + + @Test + fun testUInts() { + listOf( + UInt.MIN_VALUE, + 42.toUInt(), + UInt.MAX_VALUE, + ).forEach { value -> + testFilters( + attr(value), + "foo = :v0" to { sortKey eq value }, + "foo <> :v0" to { sortKey neq value }, + "foo < :v0" to { sortKey lt value }, + "foo <= :v0" to { sortKey lte value }, + "foo > :v0" to { sortKey gt value }, + "foo >= :v0" to { sortKey gte value }, + ) + } + + testFilters( + mapOf( + ":v0" to attr(100.toUInt()), + ":v1" to attr(200.toUInt()), + ), + "foo BETWEEN :v0 AND :v1" to { sortKey isIn 100.toUInt().rangeTo(200.toUInt()) }, + ) + } + + @Test + fun testULongs() { + listOf( + ULong.MIN_VALUE, + 42.toULong(), + ULong.MAX_VALUE, + ).forEach { value -> + testFilters( + attr(value), + "foo = :v0" to { sortKey eq value }, + "foo <> :v0" to { sortKey neq value }, + "foo < :v0" to { sortKey lt value }, + "foo <= :v0" to { sortKey lte value }, + "foo > :v0" to { sortKey gt value }, + "foo >= :v0" to { sortKey gte value }, + ) + } + + testFilters( + mapOf( + ":v0" to attr(100.toULong()), + ":v1" to attr(200.toULong()), + ), + "foo BETWEEN :v0 AND :v1" to { sortKey isIn 100.toULong().rangeTo(200.toULong()) }, + ) + } + + @Test + fun testUShorts() { + listOf( + UShort.MIN_VALUE, + 42.toUShort(), + UShort.MAX_VALUE, + ).forEach { value -> + testFilters( + attr(value), + "foo = :v0" to { sortKey eq value }, + "foo <> :v0" to { sortKey neq value }, + "foo < :v0" to { sortKey lt value }, + "foo <= :v0" to { sortKey lte value }, + "foo > :v0" to { sortKey gt value }, + "foo >= :v0" to { sortKey gte value }, + ) + } + + testFilters( + mapOf( + ":v0" to attr(100.toUShort()), + ":v1" to attr(200.toUShort()), + ), + "foo BETWEEN :v0 AND :v1" to { sortKey isIn UShortRange(100.toUShort(), 200.toUShort()) }, + ) + } + + private fun testFilters(expectedAV: AttributeValue, vararg tests: Pair SortKeyExpr>) = + testFilters(mapOf(":v0" to expectedAV), *tests) + + private fun testFilters( + expectedAVs: Map?, + vararg tests: Pair SortKeyExpr>, + expectedANs: Map? = null, + ) = tests.forEach { (expectedExprString, block) -> + val parameterizer = SortKeyExpressionVisitor() + val expr = SortKeyFilterImpl.block() + val actualExprString = expr.accept(parameterizer) + + assertEquals(expectedExprString, actualExprString) + + val actualAVs = parameterizer.expressionAttributeValues() + assertEquals(expectedAVs, actualAVs) + + val actualANs = parameterizer.expressionAttributeNames() + assertEquals(expectedANs, actualANs) + } +} + +private class SortKeyExpressionVisitor : ParameterizingExpressionVisitor() { + override fun visit(expr: AttributePath) = when (expr) { + SkAttrPathImpl -> "foo" // Swap out dummy attr path for "foo" (normally KeyFilter.toExpression would do this) + else -> super.visit(expr) + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt index 0ffd41e81db..fce2dedfdfd 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/QueryTest.kt @@ -4,22 +4,18 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.operations +import aws.sdk.kotlin.hll.dynamodbmapper.expressions.KeyFilter import aws.sdk.kotlin.hll.dynamodbmapper.items.AttributeDescriptor 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.itemOf -import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.Interceptor -import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.LReqContext import aws.sdk.kotlin.hll.dynamodbmapper.testutils.DdbLocalTest import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.IntConverter import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.StringConverter import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import kotlin.test.assertContentEquals -import aws.sdk.kotlin.services.dynamodb.model.QueryRequest as LowLevelQueryRequest - -// FIXME Much of this test class is temporary because Query/Scan don't yet have conditions class QueryTest : DdbLocalTest() { companion object { @@ -109,11 +105,11 @@ class QueryTest : DdbLocalTest() { @Test fun testQueryTable() = runTest { - val mapper = mapper { interceptors += ExpressionAttributeInterceptor("c" to "foo-corp") } + val mapper = mapper() val table = mapper.getTable(TABLE_NAME, namedEmpSchema) val items = table.queryPaginated { - keyConditionExpression = """companyId = :c""" // FIXME ugly hack until conditions are implemented + keyCondition = KeyFilter("foo-corp") }.items().toList() val expected = listOf( @@ -143,14 +139,73 @@ class QueryTest : DdbLocalTest() { assertContentEquals(expected, items) } + @Test + fun testQueryTableWithSortKeyCondition() = runTest { + val mapper = mapper() + val table = mapper.getTable(TABLE_NAME, namedEmpSchema) + + val items = table.queryPaginated { + keyCondition = KeyFilter("foo-corp") { sortKey startsWith "AB0" } + }.items().toList() + + val expected = listOf( + NamedEmp( + companyId = "foo-corp", + empId = "AB0123", + name = "Alice Birch", + title = "SDE", + tenureYears = 5, + ), + NamedEmp( + companyId = "foo-corp", + empId = "AB0126", + name = "Adriana Beech", + title = "Manager", + tenureYears = 7, + ), + ) + + assertContentEquals(expected, items) + } + + @Test + fun testQueryTableWithFilter() = runTest { + val mapper = mapper() + val table = mapper.getTable(TABLE_NAME, namedEmpSchema) + + val items = table.queryPaginated { + keyCondition = KeyFilter("foo-corp") + filter { attr("title") eq "SDE" } + }.items().toList() + + val expected = listOf( + NamedEmp( + companyId = "foo-corp", + empId = "AB0123", + name = "Alice Birch", + title = "SDE", + tenureYears = 5, + ), + NamedEmp( + companyId = "foo-corp", + empId = "EF0124", + name = "Eddie Fraser", + title = "SDE", + tenureYears = 3, + ), + ) + + assertContentEquals(expected, items) + } + @Test fun testQueryGsi() = runTest { - val mapper = mapper { interceptors += ExpressionAttributeInterceptor("t" to "Manager") } + val mapper = mapper() val table = mapper.getTable(TABLE_NAME, namedEmpSchema) val index = table.getIndex(TITLE_INDEX_NAME, titleSchema) val items = index.queryPaginated { - keyConditionExpression = """title = :t""" // FIXME ugly hack until conditions are implemented + keyCondition = KeyFilter("Manager") }.items().toList() val expected = listOf( @@ -173,12 +228,12 @@ class QueryTest : DdbLocalTest() { @Test fun testQueryLsi() = runTest { - val mapper = mapper { interceptors += ExpressionAttributeInterceptor("c" to "foo-corp") } + val mapper = mapper() val table = mapper.getTable(TABLE_NAME, namedEmpSchema) val index = table.getIndex(NAME_INDEX_NAME, empsByNameSchema) val items = index.queryPaginated { - keyConditionExpression = """companyId = :c""" // FIXME ugly hack until conditions are implemented + keyCondition = KeyFilter("foo-corp") }.items().toList() val expected = listOf( @@ -208,14 +263,3 @@ class QueryTest : DdbLocalTest() { assertContentEquals(expected, items) } } - -// FIXME ugly hack until conditions are implemented -private class ExpressionAttributeInterceptor( - vararg attributeValues: Pair, -) : Interceptor { - - val attributeValues = itemOf(*attributeValues).mapKeys { (k, _) -> ":$k" } - - override fun modifyBeforeInvocation(ctx: LReqContext): LowLevelQueryRequest = - ctx.lowLevelRequest.copy { expressionAttributeValues = attributeValues } -} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanTest.kt new file mode 100644 index 00000000000..8758d4f0b99 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/operations/ScanTest.kt @@ -0,0 +1,129 @@ +/* + * 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.items.AttributeDescriptor +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.itemOf +import aws.sdk.kotlin.hll.dynamodbmapper.testutils.DdbLocalTest +import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.IntConverter +import aws.sdk.kotlin.hll.dynamodbmapper.values.scalars.StringConverter +import aws.sdk.kotlin.hll.dynamodbmapper.values.smithytypes.InstantConverter +import aws.smithy.kotlin.runtime.time.Instant +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toSet +import kotlinx.coroutines.test.runTest +import kotlin.test.assertEquals + +class ScanTest : DdbLocalTest() { + companion object { + private const val TABLE_NAME = "scan-test" + + private data class Product( + var id: Int = 0, + var name: String = "", + var modelNumber: String = "", + var released: Instant = Instant.now(), + ) + + private val converter = SimpleItemConverter( + ::Product, + { this }, + AttributeDescriptor("id", Product::id, Product::id::set, IntConverter), + AttributeDescriptor("name", Product::name, Product::name::set, StringConverter), + AttributeDescriptor("modelNumber", Product::modelNumber, Product::modelNumber::set, StringConverter), + AttributeDescriptor("released", Product::released, Product::released::set, InstantConverter.Iso8601), + ) + + private val schema = ItemSchema(converter, KeySpec.Number("id")) + } + + @BeforeAll + fun setUp() = runTest { + createTable( + name = TABLE_NAME, + schema = schema, + itemOf( + "id" to 1, + "name" to "Standard Widget", + "modelNumber" to "wid-001", + "released" to "2024-09-24T10:15:23Z", + ), + itemOf( + "id" to 2, + "name" to "Widget Plus+", + "modelNumber" to "wid-010", + "released" to "2024-09-25T10:15:23Z", + ), + itemOf( + "id" to 3, + "name" to "Gizmo", + "modelNumber" to "GIZ.m0", + "released" to "2024-09-20T10:15:23Z", + ), + itemOf( + "id" to 4, + "name" to "Thingy", + "modelNumber" to "T1", + "released" to "2024-09-19T10:15:23Z", + ), + itemOf( + "id" to 5, + "name" to "Doohickey", + "modelNumber" to "doohick/x", + "released" to "2024-09-21T10:15:23Z", + ), + ) + } + + @Test + fun testScanTable() = runTest { + val mapper = mapper() + val table = mapper.getTable(TABLE_NAME, schema) + + table.scanPaginated { }.assertItems("Standard Widget", "Widget Plus+", "Gizmo", "Thingy", "Doohickey") + } + + @Test + fun testScanTableWithComparisonFilter() = runTest { + val mapper = mapper() + val table = mapper.getTable(TABLE_NAME, schema) + + table.scanPaginated { + filter { attr("id") gt 2 } + }.assertItems("Gizmo", "Thingy", "Doohickey") + } + + @Test + fun testScanTableWithRangeFilter() = runTest { + val mapper = mapper() + val table = mapper.getTable(TABLE_NAME, schema) + + table.scanPaginated { + filter { attr("released") isIn "2024-09-21T00:00:00Z".."2024-09-25T00:00:00Z" } + }.assertItems("Standard Widget", "Doohickey") + } + + @Test + fun testScanTableWithSizeFilter() = runTest { + val mapper = mapper() + val table = mapper.getTable(TABLE_NAME, schema) + + table.scanPaginated { + filter { attr("name").size gte 10 } + }.assertItems("Standard Widget", "Widget Plus+") + } + + private suspend fun Flow>.assertItems(vararg names: String) { + // Use sets for comparison because DDB partition keys aren't always sorted the way one might expect + val expected = names.toSet() + val actual = items().map { it.name }.toSet() + + assertEquals(expected, actual) + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/testutils/Ranges.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/testutils/Ranges.kt new file mode 100644 index 00000000000..1b7107b4005 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/testutils/Ranges.kt @@ -0,0 +1,10 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.testutils + +// Weirdly, Kotlin stdlib doesn't have range implementations for UByte and UShort (but it _does_ have UInt and ULong). +// So we're rolling our own here! +data class UByteRange(override val start: UByte, override val endInclusive: UByte) : ClosedRange +data class UShortRange(override val start: UShort, override val endInclusive: UShort) : ClosedRange diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/DslInfo.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/DslInfo.kt new file mode 100644 index 00000000000..6c9237c7497 --- /dev/null +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/DslInfo.kt @@ -0,0 +1,23 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.codegen.model + +import aws.sdk.kotlin.runtime.InternalSdkApi + +/** + * Contains information about types relevant to generating DSL methods + * @param interfaceType The interface type used as the receiver for a DSL block. This should generally be a `public` + * type. + * @param implType The implementation type used to actually invoke the DSL block. This should generally be an `internal` + * type. + * @param implSingleton A flag indicating whether [implType] is a "singleton type" that can be referenced without + * instantiation + */ +@InternalSdkApi +public data class DslInfo(val interfaceType: TypeRef, val implType: TypeRef, val implSingleton: Boolean = false) + +@InternalSdkApi +public val Member.dslInfo: DslInfo? + get() = attributes.getOrNull(ModelAttributes.DslInfo) diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Member.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Member.kt index 38e824e786c..1d83f9245f5 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Member.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Member.kt @@ -5,15 +5,25 @@ package aws.sdk.kotlin.hll.codegen.model import aws.sdk.kotlin.runtime.InternalSdkApi +import aws.smithy.kotlin.runtime.collections.Attributes +import aws.smithy.kotlin.runtime.collections.emptyAttributes +import aws.smithy.kotlin.runtime.collections.get import com.google.devtools.ksp.symbol.KSPropertyDeclaration /** * Describes a member (i.e., component field, attribute, property, etc.) of a [Structure] * @param name The name of the member inside its parent [Structure] * @param type The [Type] of the member + * @param mutable Whether the member is a mutable (`var`) property or an immutable (`val`) property + * @param attributes An [Attributes] collection for associating typed attributes with this member */ @InternalSdkApi -public data class Member(val name: String, val type: Type, val mutable: Boolean = false) { +public data class Member( + val name: String, + val type: Type, + val mutable: Boolean = false, + val attributes: Attributes = emptyAttributes(), +) { @InternalSdkApi public companion object { /** @@ -30,3 +40,10 @@ public data class Member(val name: String, val type: Type, val mutable: Boolean } } } + +/** + * Gets the low-level [Structure] equivalent for this high-level structure + */ +@InternalSdkApi +public val Member.lowLevel: Member + get() = attributes[ModelAttributes.LowLevelMember] diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/ModelAttributes.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/ModelAttributes.kt new file mode 100644 index 00000000000..c99236819a7 --- /dev/null +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/ModelAttributes.kt @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.codegen.model + +import aws.sdk.kotlin.runtime.InternalSdkApi +import aws.smithy.kotlin.runtime.collections.AttributeKey + +/** + * Defines [AttributeKey] instances that relate to the data model of low-level to high-level codegen + */ +@InternalSdkApi +public object ModelAttributes { + /** + * The types involved for a DSL-style method for working with a complex member, if applicable + */ + public val DslInfo: AttributeKey = AttributeKey("aws.sdk.kotlin.hll#DslInfo") + + /** + * For a given high-level [Member], this attribute key identifies the associated low-level [Member] + */ + public val LowLevelMember: AttributeKey = AttributeKey("aws.sdk.kotlin.hll#LowLevelMember") + + /** + * For a given high-level [Operation], this attribute key identifies the associated low-level [Operation] + */ + public val LowLevelOperation: AttributeKey = AttributeKey("aws.sdk.kotlin.hll#LowLevelOperation") + + /** + * For a given high-level [Structure], this attribute key identifies the associated low-level [Structure] + */ + public val LowLevelStructure: AttributeKey = AttributeKey("aws.sdk.kotlin.hll#LowLevelStructure") +} diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Operation.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Operation.kt index 29452620005..95cb6ce1a73 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Operation.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Operation.kt @@ -8,6 +8,7 @@ import aws.sdk.kotlin.hll.codegen.util.capitalizeFirstChar import aws.sdk.kotlin.runtime.InternalSdkApi import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.collections.emptyAttributes +import aws.smithy.kotlin.runtime.collections.get import com.google.devtools.ksp.symbol.KSFunctionDeclaration /** @@ -47,3 +48,10 @@ public data class Operation( } } } + +/** + * Gets the low-level [Operation] equivalent for this high-level operation + */ +@InternalSdkApi +public val Operation.lowLevel: Operation + get() = attributes[ModelAttributes.LowLevelOperation] diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Structure.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Structure.kt index 13e596d6ea0..a83f1308ebd 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Structure.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Structure.kt @@ -7,6 +7,7 @@ package aws.sdk.kotlin.hll.codegen.model import aws.sdk.kotlin.runtime.InternalSdkApi import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.collections.emptyAttributes +import aws.smithy.kotlin.runtime.collections.get import com.google.devtools.ksp.getDeclaredProperties import com.google.devtools.ksp.symbol.KSClassDeclaration import com.google.devtools.ksp.symbol.KSTypeReference @@ -51,3 +52,10 @@ public fun Structure.genericVars(): List = buildList { addAll(type.genericVars()) members.flatMap { it.type.genericVars() }.let(::addAll) } + +/** + * Gets the low-level [Structure] equivalent for this high-level structure + */ +@InternalSdkApi +public val Structure.lowLevel: Structure + get() = attributes[ModelAttributes.LowLevelStructure] diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt index b86839791fa..d3a543e8a83 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt @@ -94,15 +94,32 @@ public data class TypeVar(override val shortName: String, override val nullable: * Derives a nullable [Type] equivalent for this type */ @InternalSdkApi -public fun Type.nullable(): Type = when { - nullable -> this - this is TypeRef -> copy(nullable = true) - this is TypeVar -> copy(nullable = true) +public fun Type.nullable(value: Boolean = true): Type = when { + nullable == value -> this + this is TypeRef -> copy(nullable = value) + this is TypeVar -> copy(nullable = value) else -> error("Unknown Type ${this::class}") // Should be unreachable, only here to make compiler happy } /** -<<<<<<< HEAD + * Derives a nullable [TypeRef] equivalent for this type reference + */ +@InternalSdkApi +public fun TypeRef.nullable(value: Boolean = true): TypeRef = when { + nullable == value -> this + else -> copy(nullable = value) +} + +/** + * Derives a nullable [TypeVar] equivalent for this type variable + */ +@InternalSdkApi +public fun TypeVar.nullable(value: Boolean = true): TypeVar = when { + nullable == value -> this + else -> copy(nullable = value) +} + +/** * Gets a collection of all generic variables referenced by this [Type] */ @InternalSdkApi diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt index d3cae17d13d..52bbb1faa0e 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt @@ -29,7 +29,7 @@ public object Types { public val OptIn: TypeRef = kotlin("OptIn") public val Short: TypeRef = kotlin("Short") public val String: TypeRef = kotlin("String") - public val StringNullable: TypeRef = String.nullable() as TypeRef + public val StringNullable: TypeRef = String.nullable() public val UByte: TypeRef = kotlin("UByte") public val UInt: TypeRef = kotlin("UInt") public val ULong: TypeRef = kotlin("ULong") diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/rendering/BuilderRenderer.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/rendering/BuilderRenderer.kt index 1508a41eb3c..69a2b58ca69 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/rendering/BuilderRenderer.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/rendering/BuilderRenderer.kt @@ -61,8 +61,30 @@ public class BuilderRenderer( blankLine() } - private fun renderProperty(member: Member) = generator.apply { + private fun renderProperty(member: Member) { + val dslInfo = member.dslInfo + + if (dslInfo != null) blankLine() + write("#Lvar #L: #T = null", ctx.attributes.visibility, member.name, member.type.nullable()) - // TODO add DSL methods for structure members + + ctx.info("For member $builderName.${member.name} dslInfo = $dslInfo") + if (dslInfo != null) { + blankLine() + withBlock( + "#Lfun #L(block: #T.() -> #T) {", + "}", + ctx.attributes.visibility, + member.name, + dslInfo.interfaceType, + member.type, + ) { + val constructorIfNecessary = if (dslInfo.implSingleton) "" else "()" + write("#L = #T#L.run(block)", member.name, dslInfo.implType, constructorIfNecessary) + } + blankLine() + } + + // TODO add DSL methods for low-level structure members } }