Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix KeyConditionExpression rendering for names #245

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions dynamodb/src/it/scala/zio/dynamodb/LiveSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import software.amazon.awssdk.services.dynamodb.model.{ DynamoDbException, Idemp
import zio.dynamodb.UpdateExpression.Action.SetAction
import zio.dynamodb.UpdateExpression.SetOperand
import zio.dynamodb.PartitionKeyExpression.PartitionKey
import zio.dynamodb.KeyConditionExpression.partitionKey
import zio.dynamodb.KeyConditionExpression.sortKey
import zio.dynamodb.SortKeyExpression.SortKey
import zio.aws.{ dynamodb, netty }
import zio._
Expand Down Expand Up @@ -116,6 +118,12 @@ object LiveSpec extends ZIOSpecDefault {
AttributeDefinition.attrDefnString(name)
)

def sortKeyStringTableWithKeywords(tableName: String) =
createTable(tableName, KeySchema("and", "source"), BillingMode.PayPerRequest)(
AttributeDefinition.attrDefnString("and"),
AttributeDefinition.attrDefnString("source")
)

private def managedTable(tableDefinition: String => CreateTable) =
ZIO
.acquireRelease(
Expand Down Expand Up @@ -150,6 +158,17 @@ object LiveSpec extends ZIOSpecDefault {
}
}

def withPkKeywordsTable(
f: String => ZIO[DynamoDBExecutor, Throwable, TestResult]
) =
ZIO.scoped {
managedTable(sortKeyStringTableWithKeywords).flatMap { table =>
for {
result <- f(table.value)
} yield result
}
}

def withDefaultAndNumberTables(
f: (String, String) => ZIO[DynamoDBExecutor, Throwable, TestResult]
) =
Expand Down Expand Up @@ -179,8 +198,65 @@ object LiveSpec extends ZIOSpecDefault {
val (id, num, ttl) = ProjectionExpression.accessors[ExpressionAttrNames]
}

final case class ExpressionAttrNamesPkKeywords(and: String, source: String, ttl: Option[Long])
object ExpressionAttrNamesPkKeywords {
implicit val schema: Schema.CaseClass3[String, String, Option[Long], ExpressionAttrNamesPkKeywords] =
DeriveSchema.gen[ExpressionAttrNamesPkKeywords]
val (and, source, ttl) = ProjectionExpression.accessors[ExpressionAttrNamesPkKeywords]
}

val mainSuite: Spec[TestEnvironment, Any] =
suite("live test")(
suite("key words in Key Condition Expressions")(
test("queryAll should handle keywords in primary key names using high level API") {
withPkKeywordsTable { tableName =>
val query = DynamoDBQuery
.queryAll[ExpressionAttrNamesPkKeywords](tableName)
.whereKey(
ExpressionAttrNamesPkKeywords.and === "and1" && ExpressionAttrNamesPkKeywords.source === "source1"
)
.filter(ExpressionAttrNamesPkKeywords.ttl.notExists)
query.execute.flatMap(_.runDrain).exit.map { result =>
assert(result)(succeeds(isUnit))
}
}
},
test("queryAll should handle keywords in primary key name using low level API") {
withPkKeywordsTable { tableName =>
val query = DynamoDBQuery
.queryAll[ExpressionAttrNamesPkKeywords](tableName)
.whereKey(partitionKey("and") === "and1" && sortKey("source") === "source1")
.filter(ExpressionAttrNamesPkKeywords.ttl.notExists)
query.execute.flatMap(_.runDrain).exit.map { result =>
assert(result)(succeeds(isUnit))
}
}
},
test("querySome should handle keywords in primary key name using high level API") {
withPkKeywordsTable { tableName =>
val query = DynamoDBQuery
.querySome[ExpressionAttrNamesPkKeywords](tableName, 1)
.whereKey(
ExpressionAttrNamesPkKeywords.and === "and1" && ExpressionAttrNamesPkKeywords.source === "source1"
)
.filter(ExpressionAttrNamesPkKeywords.ttl.notExists)
for {
result <- query.execute
} yield assert(result._1)(hasSize(equalTo(0)))
}
},
test("querySome should handle keywords in primary key name using low level API") {
withPkKeywordsTable { tableName =>
val query = DynamoDBQuery
.querySome[ExpressionAttrNames](tableName, 1)
.whereKey(partitionKey("and") === "and1" && sortKey("source") === "source1")
.filter(ExpressionAttrNames.ttl.notExists)
for {
result <- query.execute
} yield assert(result._1)(hasSize(equalTo(0)))
}
}
),
suite("keywords in expression attribute names")(
suite("using high level api")(
test("scanAll should handle keyword") {
Expand Down
86 changes: 42 additions & 44 deletions dynamodb/src/main/scala/zio/dynamodb/KeyConditionExpression.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ sealed trait KeyConditionExpression extends Renderable { self =>
}

object KeyConditionExpression {

def getOrInsert[From, To](primaryKeyName: String): AliasMapRender[String] =
AliasMapRender.getOrInsert(ProjectionExpression.MapElement[From, To](Root, primaryKeyName))
private[dynamodb] final case class And(left: PartitionKeyExpression, right: SortKeyExpression)
extends KeyConditionExpression
def partitionKey(key: String): PartitionKey = PartitionKey(key)
def partitionKey(key: String): PartitionKey = PartitionKey(key)
def sortKey(key: String): SortKey = SortKey(key)

/**
* Create a KeyConditionExpression from a ConditionExpression
Expand Down Expand Up @@ -156,7 +160,10 @@ sealed trait PartitionKeyExpression extends KeyConditionExpression { self =>
override def render: AliasMapRender[String] =
self match {
case PartitionKeyExpression.Equals(left, right) =>
AliasMapRender.getOrInsert(right).map(v => s"${left.keyName} = $v")
for {
v <- AliasMapRender.getOrInsert(right)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} = $v"
}
}
object PartitionKeyExpression {
Expand All @@ -171,55 +178,46 @@ sealed trait SortKeyExpression { self =>
def render: AliasMapRender[String] =
self match {
case SortKeyExpression.Equals(left, right) =>
AliasMapRender
.getOrInsert(right)
.map { v =>
s"${left.keyName} = $v"
}
for {
v <- AliasMapRender.getOrInsert(right)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} = $v"
case SortKeyExpression.LessThan(left, right) =>
AliasMapRender
.getOrInsert(right)
.map { v =>
s"${left.keyName} < $v"
}
for {
v <- AliasMapRender.getOrInsert(right)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} < $v"
case SortKeyExpression.NotEqual(left, right) =>
AliasMapRender
.getOrInsert(right)
.map { v =>
s"${left.keyName} <> $v"
}
for {
v <- AliasMapRender.getOrInsert(right)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} <> $v"
case SortKeyExpression.GreaterThan(left, right) =>
AliasMapRender
.getOrInsert(right)
.map { v =>
s"${left.keyName} > $v"
}
for {
v <- AliasMapRender.getOrInsert(right)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} > $v"
case SortKeyExpression.LessThanOrEqual(left, right) =>
AliasMapRender
.getOrInsert(right)
.map { v =>
s"${left.keyName} <= $v"
}
for {
v <- AliasMapRender.getOrInsert(right)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} <= $v"
case SortKeyExpression.GreaterThanOrEqual(left, right) =>
AliasMapRender
.getOrInsert(right)
.map { v =>
s"${left.keyName} >= $v"
}
for {
v <- AliasMapRender.getOrInsert(right)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} >= $v"
case SortKeyExpression.Between(left, min, max) =>
AliasMapRender
.getOrInsert(min)
.flatMap(min =>
AliasMapRender.getOrInsert(max).map { max =>
s"${left.keyName} BETWEEN $min AND $max"
}
)
for {
min2 <- AliasMapRender.getOrInsert(min)
max2 <- AliasMapRender.getOrInsert(max)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"${keyName} BETWEEN $min2 AND $max2"
case SortKeyExpression.BeginsWith(left, value) =>
AliasMapRender
.getOrInsert(value)
.map { v =>
s"begins_with(${left.keyName}, $v)"
}
for {
v <- AliasMapRender.getOrInsert(value)
keyName <- KeyConditionExpression.getOrInsert(left.keyName)
} yield s"begins_with(${keyName}, $v)"
}

}
Expand Down
Loading
Loading