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

minor TestDynamoDBExecutor improvements #460

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
16 changes: 8 additions & 8 deletions dynamodb/src/main/scala/zio/dynamodb/DynamoDBExecutor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@ object DynamoDBExecutor {
val effect = for {
ref <- Ref.make(List.empty[DynamoDBQuery[_, _]])
test <- (for {
tableMap <- TMap.empty[String, TMap[PrimaryKey, Item]]
tablePkNameMap <- TMap.empty[String, String]
tableMap <- TMap.empty[TableName, TMap[PrimaryKey, Item]]
tablePkNameMap <- TMap.empty[TableName, String]
} yield TestDynamoDBExecutorImpl(ref, tableMap, tablePkNameMap)).commit
} yield test
ZLayer.fromZIO(effect)
}

def test(tableDefs: TableNameAndPK*): ULayer[DynamoDBExecutor with TestDynamoDBExecutor] = {
def test(tableAndPKNames: (String, String)*): ULayer[DynamoDBExecutor with TestDynamoDBExecutor] = {
val effect = for {
ref <- Ref.make(List.empty[DynamoDBQuery[_, _]])
test <- (for {
tableMap <- TMap.empty[String, TMap[PrimaryKey, Item]]
tablePkNameMap <- TMap.empty[String, String]
_ <- STM.foreach(tableDefs) {
tableMap <- TMap.empty[TableName, TMap[PrimaryKey, Item]]
tablePkNameMap <- TMap.empty[TableName, String]
_ <- STM.foreach(tableAndPKNames) {
case (tableName, pkFieldName) =>
for {
_ <- tablePkNameMap.put(tableName, pkFieldName)
_ <- tablePkNameMap.put(TableName(tableName), pkFieldName)
pkToItemMap <- TMap.empty[PrimaryKey, Item]
_ <- tableMap.put(tableName, pkToItemMap)
_ <- tableMap.put(TableName(tableName), pkToItemMap)
} yield ()
}
} yield TestDynamoDBExecutorImpl(ref, tableMap, tablePkNameMap)).commit
Expand Down
21 changes: 5 additions & 16 deletions dynamodb/src/main/scala/zio/dynamodb/TestDynamoDBExecutor.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package zio.dynamodb

import zio.dynamodb.TestDynamoDBExecutor.PkAndItem
import zio.{ UIO, ZIO }
import zio.dynamodb.DynamoDBQuery.{ BatchGetItem, BatchWriteItem }

/**
* A Fake implementation of `DynamoDBExecutor.Service` that currently has the very modest aspiration of providing bare minimum
Expand Down Expand Up @@ -34,10 +34,11 @@ import zio.dynamodb.DynamoDBQuery.{ BatchGetItem, BatchWriteItem }
trait TestDynamoDBExecutor {
def addTable(tableName: String, pkFieldName: String, pkAndItems: PkAndItem*): UIO[Unit]
def addItems(tableName: String, pkAndItems: PkAndItem*): ZIO[Any, DynamoDBError, Unit]
def queries: UIO[List[DynamoDBQuery[_, _]]]
def recordedQueries: UIO[List[DynamoDBQuery[_, _]]]
}

object TestDynamoDBExecutor {
type PkAndItem = (PrimaryKey, Item)

def addTable(
tableName: String,
Expand All @@ -49,19 +50,7 @@ object TestDynamoDBExecutor {
def addItems(tableName: String, pkAndItems: PkAndItem*): ZIO[TestDynamoDBExecutor, DynamoDBError, Unit] =
ZIO.serviceWithZIO[TestDynamoDBExecutor](_.addItems(tableName, pkAndItems: _*))

def queries: ZIO[TestDynamoDBExecutor, Nothing, List[DynamoDBQuery[_, _]]] =
ZIO.serviceWithZIO[TestDynamoDBExecutor](_.queries)

def isEmptyBatchWrite(q: DynamoDBQuery[_, _]) =
q match {
case BatchWriteItem(items, _, _, _, _) => items.isEmpty
case _ => false
}

def isEmptyBatchGet(q: DynamoDBQuery[_, _]) =
q match {
case BatchGetItem(items, _, _, _) => items.isEmpty
case _ => false
}
def recordedQueries: ZIO[TestDynamoDBExecutor, Nothing, List[DynamoDBQuery[_, _]]] =
ZIO.serviceWithZIO[TestDynamoDBExecutor](_.recordedQueries)

}
53 changes: 27 additions & 26 deletions dynamodb/src/main/scala/zio/dynamodb/TestDynamoDBExecutorImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ package zio.dynamodb

import zio.dynamodb.DynamoDBQuery.BatchGetItem.TableGet
import zio.dynamodb.DynamoDBQuery._
import zio.dynamodb.TestDynamoDBExecutor.PkAndItem
import zio.stm.{ STM, TMap, ZSTM }
import zio.stream.{ Stream, ZStream }
import zio.{ Chunk, IO, Ref, UIO, ZIO }
import scala.annotation.nowarn

@nowarn
private[dynamodb] final case class TestDynamoDBExecutorImpl private[dynamodb] (
recordedQueries: Ref[List[DynamoDBQuery[_, _]]],
tableMap: TMap[String, TMap[PrimaryKey, Item]],
tablePkNameMap: TMap[String, String]
queries: Ref[List[DynamoDBQuery[_, _]]],
tableMap: TMap[TableName, TMap[PrimaryKey, Item]],
tablePkNameMap: TMap[TableName, String]
) extends DynamoDBExecutor
with TestDynamoDBExecutor {
self =>
Expand All @@ -26,7 +27,7 @@ private[dynamodb] final case class TestDynamoDBExecutorImpl private[dynamodb] (
.foreach(requestItems) {
case (tableName, tableGet) =>
ZIO.foreach(tableGet.keysSet) { key =>
fakeGetItem(tableName.value, key).map((tableName, _))
fakeGetItem(tableName, key).map((tableName, _))
}
}
.map(_.flatten)
Expand All @@ -47,51 +48,51 @@ private[dynamodb] final case class TestDynamoDBExecutorImpl private[dynamodb] (
ZIO.foreachDiscard(setOfWrite) { write =>
write match {
case BatchWriteItem.Put(item) =>
fakePut(tableName.value, item)
fakePut(tableName, item)
case BatchWriteItem.Delete(pk) =>
fakeDelete(tableName.value, pk)
fakeDelete(tableName, pk)
}
}

}
results.map(_ => BatchWriteItem.Response(None))

case GetItem(tableName, key, _, _, _, _) =>
fakeGetItem(tableName.value, key)
fakeGetItem(tableName, key)

case PutItem(tableName, item, _, _, _, _, _) =>
fakePut(tableName.value, item)
fakePut(tableName, item)

// TODO Note UpdateItem is not currently supported as it uses an UpdateExpression

case DeleteItem(tableName, key, _, _, _, _, _) =>
fakeDelete(tableName.value, key)
fakeDelete(tableName, key)

case ScanSome(tableName, limit, _, _, exclusiveStartKey, _, _, _, _) =>
fakeScanSome(tableName.value, exclusiveStartKey, Some(limit))
fakeScanSome(tableName, exclusiveStartKey, Some(limit))

case ScanAll(tableName, _, maybeLimit, _, _, _, _, _, _, _) =>
fakeScanAll(tableName.value, maybeLimit)
fakeScanAll(tableName, maybeLimit)

case QuerySome(tableName, limit, _, _, exclusiveStartKey, _, _, _, _, _, _) =>
fakeScanSome(tableName.value, exclusiveStartKey, Some(limit))
fakeScanSome(tableName, exclusiveStartKey, Some(limit))

case QueryAll(tableName, _, maybeLimit, _, _, _, _, _, _, _, _) =>
fakeScanAll(tableName.value, maybeLimit)
fakeScanAll(tableName, maybeLimit)

// TODO: implement CreateTable

case unknown =>
ZIO.die(new Exception(s"Constructor $unknown not implemented yet"))
}

recordedQueries.update(_ :+ query) *> result
queries.update(_ :+ query) *> result
}

private def tableError(tableName: String): DynamoDBError =
private def tableError(tableName: TableName): DynamoDBError =
DynamoDBError.ItemError.ValueNotFound(s"table $tableName does not exist")

private def tableMapAndPkName(tableName: String): ZSTM[Any, DynamoDBError, (TMap[PrimaryKey, Item], String)] =
private def tableMapAndPkName(tableName: TableName): ZSTM[Any, DynamoDBError, (TMap[PrimaryKey, Item], String)] =
for {
tableMap <- for {
maybeTableMap <- self.tableMap.get(tableName)
Expand All @@ -106,30 +107,30 @@ private[dynamodb] final case class TestDynamoDBExecutorImpl private[dynamodb] (
.flatMap(_.fold[STM[DynamoDBError, String]](STM.fail(tableError(tableName)))(STM.succeed(_)))
} yield (tableMap, pkName)

private def pkForItem(item: Item, pkName: String): PrimaryKey =
private def pkForItem(item: Item, pkName: String): PrimaryKey =
Item(item.map.filter { case (key, _) => key == pkName })

private def fakeGetItem(tableName: String, pk: PrimaryKey): IO[DynamoDBError, Option[Item]] =
private def fakeGetItem(tableName: TableName, pk: PrimaryKey): IO[DynamoDBError, Option[Item]] =
(for {
(tableMap, _) <- tableMapAndPkName(tableName)
maybeItem <- tableMap.get(pk)
} yield maybeItem).commit

private def fakePut(tableName: String, item: Item): IO[DynamoDBError, Option[Item]] =
private def fakePut(tableName: TableName, item: Item): IO[DynamoDBError, Option[Item]] =
(for {
(tableMap, pkName) <- tableMapAndPkName(tableName)
pk = pkForItem(item, pkName)
_ <- tableMap.put(pk, item)
} yield None).commit

private def fakeDelete(tableName: String, pk: PrimaryKey): IO[DynamoDBError, Option[Item]] =
private def fakeDelete(tableName: TableName, pk: PrimaryKey): IO[DynamoDBError, Option[Item]] =
(for {
(tableMap, _) <- tableMapAndPkName(tableName)
_ <- tableMap.delete(pk)
} yield None).commit

private def fakeScanSome(
tableName: String,
tableName: TableName,
exclusiveStartKey: LastEvaluatedKey,
maybeLimit: Option[Int]
): IO[DynamoDBError, (Chunk[Item], LastEvaluatedKey)] =
Expand All @@ -139,7 +140,7 @@ private[dynamodb] final case class TestDynamoDBExecutorImpl private[dynamodb] (
result <- STM.succeed(slice(sort(xs, pkName), exclusiveStartKey, maybeLimit))
} yield result).commit

private def fakeScanAll[R](tableName: String, maybeLimit: Option[Int]): UIO[Stream[DynamoDBError, Item]] = {
private def fakeScanAll[R](tableName: TableName, maybeLimit: Option[Int]): UIO[Stream[DynamoDBError, Item]] = {
val start: LastEvaluatedKey = None
ZIO.succeed(
ZStream
Expand Down Expand Up @@ -217,21 +218,21 @@ private[dynamodb] final case class TestDynamoDBExecutorImpl private[dynamodb] (

override def addTable(tableName: String, pkFieldName: String, pkAndItems: PkAndItem*): UIO[Unit] =
(for {
_ <- tablePkNameMap.put(tableName, pkFieldName)
_ <- tablePkNameMap.put(TableName(tableName), pkFieldName)
tmap <- TMap.empty[PrimaryKey, Item]
_ <- STM.foreach(pkAndItems) {
case (pk, item) => tmap.put(pk, item)
}
_ <- tableMap.put(tableName, tmap)
_ <- tableMap.put(TableName(tableName), tmap)
} yield ()).commit

override def addItems(tableName: String, pkAndItems: (PrimaryKey, Item)*): ZIO[Any, DynamoDBError, Unit] =
(for {
(tableMap, _) <- tableMapAndPkName(tableName)
(tableMap, _) <- tableMapAndPkName(TableName(tableName))
_ <- STM.foreach(pkAndItems) {
case (pk, item) => tableMap.put(pk, item)
}
} yield ()).commit

override def queries: UIO[List[DynamoDBQuery[_, _]]] = self.recordedQueries.get
override def recordedQueries: UIO[List[DynamoDBQuery[_, _]]] = self.queries.get
}
3 changes: 0 additions & 3 deletions dynamodb/src/main/scala/zio/dynamodb/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ package object dynamodb {
type Item = AttrMap
val Item = AttrMap

type PkAndItem = (PrimaryKey, Item)
type TableNameAndPK = (String, String)

type Encoder[A] = A => AttributeValue
type Decoder[+A] = AttributeValue => Either[ItemError, A]

Expand Down
23 changes: 17 additions & 6 deletions dynamodb/src/test/scala/zio/dynamodb/BatchingDSLSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@ object BatchingDSLSpec extends ZIOSpecDefault with DynamoDBFixtures {
DynamoDBExecutor.test
)

private val singleQueryDoesNotBatchSuite = suite("single query does not batch CRUD suite")(
private val singleQueryDoesNotBatchSuite = suite("single query does not auto-batch CRUD suite")(
test("a single getItem does not get auto-batched") {
for {
_ <- TestDynamoDBExecutor.addTable(tableName1.value, "k1", primaryKeyT1 -> itemT1, primaryKeyT1_2 -> itemT1_2)
result <- getItemT1.execute
queries <- TestDynamoDBExecutor.queries
queries <- TestDynamoDBExecutor.recordedQueries
} yield assert(result)(equalTo(Some(itemT1))) && assertQueryNotBatched(queries)
},
test("a single putItem does not get auto-batched") {
for {
_ <- TestDynamoDBExecutor.addTable(tableName1.value, "k1", primaryKeyT1 -> itemT1, primaryKeyT1_2 -> itemT1_2)
result <- putItemT1.execute
queries <- TestDynamoDBExecutor.queries
queries <- TestDynamoDBExecutor.recordedQueries
} yield assert(result)(equalTo(None)) && assertQueryNotBatched(queries)
},
test("a single deleteItem does not get auto-batched") {
for {
_ <- TestDynamoDBExecutor.addTable(tableName1.value, "k1", primaryKeyT1 -> itemT1, primaryKeyT1_2 -> itemT1_2)
result <- deleteItemT1.execute
queries <- TestDynamoDBExecutor.queries
queries <- TestDynamoDBExecutor.recordedQueries
} yield assert(result)(equalTo(None)) && assertQueryNotBatched(queries)
}
)
Expand Down Expand Up @@ -198,9 +198,20 @@ object BatchingDSLSpec extends ZIOSpecDefault with DynamoDBFixtures {

private def assertQueryNotBatched(queries: List[DynamoDBQuery[_, _]]) =
assertTrue(queries.size == 3) && assertTrue(
queries.find(TestDynamoDBExecutor.isEmptyBatchWrite).isDefined
queries.find(isEmptyBatchWrite).isDefined
) && assertTrue(
queries.find(TestDynamoDBExecutor.isEmptyBatchGet).isDefined
queries.find(isEmptyBatchGet).isDefined
)

private def isEmptyBatchWrite(q: DynamoDBQuery[_, _]): Boolean =
q match {
case BatchWriteItem(items, _, _, _, _) => items.isEmpty
case _ => false
}

private def isEmptyBatchGet(q: DynamoDBQuery[_, _]): Boolean =
q match {
case BatchGetItem(items, _, _, _) => items.isEmpty
case _ => false
}
}
4 changes: 2 additions & 2 deletions dynamodb/src/test/scala/zio/dynamodb/DynamoDBFixtures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ trait DynamoDBFixtures {
val deleteItemT1: DeleteItem = DeleteItem(tableName = tableName1, key = primaryKeyT1)
val deleteItemT3: DeleteItem = DeleteItem(tableName = tableName3, key = primaryKeyT3)

def chunkOfPrimaryKeyAndItem(r: Range, pkFieldName: String): Chunk[PkAndItem] =
def chunkOfPrimaryKeyAndItem(r: Range, pkFieldName: String): Chunk[(PrimaryKey, Item)] =
Chunk.fromIterable(r.map(i => (PrimaryKey(pkFieldName -> i), Item(pkFieldName -> i, "k2" -> (i + 1)))).toList)

def resultItems(range: Range): Chunk[Item] = chunkOfPrimaryKeyAndItem(range, "k1").map { case (_, v) => v }
def resultItems(range: Range): Chunk[Item] = chunkOfPrimaryKeyAndItem(range, "k1").map { case (_, v) => v }

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import zio.dynamodb.{ DynamoDBExecutor, DynamoDBQuery, PrimaryKey }
import zio.schema.annotation.{ caseName, discriminatorName }
import zio.schema.{ DeriveSchema, Schema }

import java.time.Instant
import zio.dynamodb.ProjectionExpression
import zio.ZIO
import zio.dynamodb.DynamoDBError.ItemError
import zio.ZIO
import java.time.Instant

object TypeSafeRoundTripSerialisationExample extends ZIOAppDefault {

@discriminatorName("invoiceType")
Expand Down
Loading