From 635dff906f564e61239e51ea2310f6f237f79879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bendix=20S=C3=A4ltz?= Date: Thu, 7 Oct 2021 08:44:10 +0200 Subject: [PATCH] Support DynamoDB's `UpdateItemRequest` (#288) * Add decodeItemResponse() * Use Long.MaxValue instead of magic number --- src/main/scala/scynamo/Scynamo.scala | 9 ++++++-- .../scala/scynamo/ScynamoCodecProps.scala | 12 +++++----- src/test/scala/scynamo/ScynamoTest.scala | 23 ++++++++++++++++++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/main/scala/scynamo/Scynamo.scala b/src/main/scala/scynamo/Scynamo.scala index 85b50002..fa048f64 100644 --- a/src/main/scala/scynamo/Scynamo.scala +++ b/src/main/scala/scynamo/Scynamo.scala @@ -1,7 +1,7 @@ package scynamo import cats.data.EitherNec -import software.amazon.awssdk.services.dynamodb.model.{GetItemResponse, QueryResponse, ScanResponse} +import software.amazon.awssdk.services.dynamodb.model.{GetItemResponse, QueryResponse, ScanResponse, UpdateItemResponse} import cats.syntax.all._ import scala.jdk.CollectionConverters._ @@ -14,10 +14,15 @@ trait ScynamoFunctions { else Right(None) + def decodeUpdateItemResponse[A: ObjectScynamoDecoder](response: UpdateItemResponse): EitherNec[ScynamoDecodeError, Option[A]] = + if (response.hasAttributes) + ObjectScynamoDecoder[A].decodeMap(response.attributes()).map(Some(_)) + else + Right(None) + def decodeQueryResponse[A: ObjectScynamoDecoder](response: QueryResponse): EitherNec[ScynamoDecodeError, List[A]] = response.items().asScala.toList.traverse(ObjectScynamoDecoder[A].decodeMap(_)) def decodeScanResponse[A: ObjectScynamoDecoder](response: ScanResponse): EitherNec[ScynamoDecodeError, List[A]] = response.items().asScala.toList.traverse(ObjectScynamoDecoder[A].decodeMap(_)) - } diff --git a/src/test/scala/scynamo/ScynamoCodecProps.scala b/src/test/scala/scynamo/ScynamoCodecProps.scala index 0ba88125..b1a28ade 100644 --- a/src/test/scala/scynamo/ScynamoCodecProps.scala +++ b/src/test/scala/scynamo/ScynamoCodecProps.scala @@ -82,15 +82,15 @@ class ScynamoCodecProps extends Properties("ScynamoCodec") { propertyWithSeed("decode.encode === id (option)", propertySeed) = Prop.forAll { value: Option[Int] => decodeAfterEncodeIsIdentity(value) } - propertyWithSeed("decode.encode === id (finite duration)", propertySeed) = - Prop.forAll(Gen.chooseNum(-9223372036854775807L, 9223372036854775807L)) { value => + propertyWithSeed("decode.encode === id (finite duration)", propertySeed) = Prop.forAll(Gen.chooseNum(Long.MinValue + 1, Long.MaxValue)) { + value: Long => decodeAfterEncodeIsIdentity(Duration.fromNanos(value)) - } + } - propertyWithSeed("decode.encode === id (duration)", propertySeed) = - Prop.forAll(Gen.chooseNum(-9223372036854775807L, 9223372036854775807L)) { value => + propertyWithSeed("decode.encode === id (duration)", propertySeed) = Prop.forAll(Gen.chooseNum(Long.MinValue + 1, Long.MaxValue)) { + value: Long => decodeAfterEncodeIsIdentity(Duration.fromNanos(value): Duration) - } + } propertyWithSeed("decode.encode === id (java duration)", propertySeed) = Prop.forAll { value: Long => decodeAfterEncodeIsIdentity(java.time.Duration.ofNanos(value)) diff --git a/src/test/scala/scynamo/ScynamoTest.scala b/src/test/scala/scynamo/ScynamoTest.scala index 4f5ba9a6..2ec91e33 100644 --- a/src/test/scala/scynamo/ScynamoTest.scala +++ b/src/test/scala/scynamo/ScynamoTest.scala @@ -17,6 +17,15 @@ class ScynamoTest extends UnitTest { result should ===(Right(None)) } + "return None if the update item response has no item" in { + val response = UpdateItemResponse.builder().build() + val result = for { + result <- Scynamo.decodeUpdateItemResponse[Map[String, AttributeValue]](response) + } yield result + + result should ===(Right(None)) + } + "return an empty List if the query response has no items" in { val response = QueryResponse.builder().build() @@ -37,7 +46,7 @@ class ScynamoTest extends UnitTest { result should ===(Right(List.empty)) } - "return the decoded result if it has an item that is well formed" in { + "return the decoded get item result if it has an item that is well formed" in { val input = Map("foo" -> "bar") val result = for { @@ -49,6 +58,18 @@ class ScynamoTest extends UnitTest { result should ===(Right(Some(input))) } + "return the decoded update item result if it has an item that is well formed" in { + val input = Map("foo" -> "bar") + + val result = for { + encodedInput <- input.encodedMap + response = UpdateItemResponse.builder().attributes(encodedInput).build() + result <- Scynamo.decodeUpdateItemResponse[Map[String, String]](response) + } yield result + + result should ===(Right(Some(input))) + } + "return the decoded query result if it has multiple items that are well formed" in { val input1 = Map("foo" -> "bar") val input2 = Map("Miami" -> "Ibiza")