diff --git a/src/main/scala/scynamo/ScynamoEncoder.scala b/src/main/scala/scynamo/ScynamoEncoder.scala index de11a8d0..f9a1ebe2 100644 --- a/src/main/scala/scynamo/ScynamoEncoder.scala +++ b/src/main/scala/scynamo/ScynamoEncoder.scala @@ -13,7 +13,7 @@ import shapeless.tag.@@ import software.amazon.awssdk.services.dynamodb.model.AttributeValue import java.time.{Instant, YearMonth} -import java.util.{Collections, UUID} +import java.util.UUID import scala.collection.compat._ import scala.collection.immutable.Seq import scala.concurrent.duration.{Duration, FiniteDuration} @@ -201,7 +201,7 @@ object ObjectScynamoEncoder extends SemiautoDerivationEncoder { } } - NonEmptyChain.fromChain(allErrors).toLeft(Collections.unmodifiableMap(attrValues)) + NonEmptyChain.fromChain(allErrors).toLeft(attrValues) } } diff --git a/src/main/scala/scynamo/generic/GenericScynamoEncoder.scala b/src/main/scala/scynamo/generic/GenericScynamoEncoder.scala index 3ca83e13..09a8a690 100644 --- a/src/main/scala/scynamo/generic/GenericScynamoEncoder.scala +++ b/src/main/scala/scynamo/generic/GenericScynamoEncoder.scala @@ -3,8 +3,6 @@ package scynamo.generic import scynamo.ObjectScynamoEncoder import shapeless.{LabelledGeneric, Lazy} -import java.util.Collections - trait GenericScynamoEncoder[A] extends ObjectScynamoEncoder[A] object GenericScynamoEncoder extends GenericScynamoEncoderInstances @@ -14,5 +12,5 @@ trait GenericScynamoEncoderInstances { gen: LabelledGeneric.Aux[F, G], sg: Lazy[ShapelessScynamoEncoder[F, G]] ): GenericScynamoEncoder[F] = - value => sg.value.encodeMap(gen.to(value)).map(Collections.unmodifiableMap(_)) + value => sg.value.encodeMap(gen.to(value)) } diff --git a/src/test/scala/scynamo/ScynamoEncoderTest.scala b/src/test/scala/scynamo/ScynamoEncoderTest.scala index 564ad62d..6c6eb2bc 100644 --- a/src/test/scala/scynamo/ScynamoEncoderTest.scala +++ b/src/test/scala/scynamo/ScynamoEncoderTest.scala @@ -1,13 +1,15 @@ package scynamo import cats.data.EitherNec -import cats.syntax.either._ +import cats.syntax.all._ import org.scalatest.Inside -import scynamo.ScynamoEncoderTest.{Foo, Foo2, Snake} +import scynamo.syntax.all._ +import scynamo.ScynamoEncoderTest._ import scynamo.StackFrame.{Attr, Case, Index, MapKey} import scynamo.wrapper.{ScynamoBinarySet, ScynamoNumberSet, ScynamoStringSet} import software.amazon.awssdk.core.SdkBytes import software.amazon.awssdk.services.dynamodb.model.AttributeValue +import scala.jdk.CollectionConverters._ class ScynamoEncoderTest extends UnitTest { "ScynamoEncoder" should { @@ -138,14 +140,49 @@ class ScynamoEncoderTest extends UnitTest { .encodeMap(Snake(1, Some(Snake(2, None)))) .map(attrs => Option(attrs.get("tail").m.get("tail"))) should ===(Right(None)) } + + "allow modifying encoded maps" in { + val prices = Typed("price", Map("milk" -> 1.29, "hummus" -> 0.99)) + prices.encodedMap.map(_.asScala) shouldBe Right( + Map( + "type" -> AttributeValue.builder.s("price").build(), + "milk" -> AttributeValue.builder.n("1.29").build(), + "hummus" -> AttributeValue.builder.n("0.99").build() + ) + ) + } + + "allow modifying encoded objects" in { + val prices = Typed("snake", Snake(42, None)) + prices.encodedMap.map(_.asScala) shouldBe Right( + Map( + "type" -> AttributeValue.builder.s("snake").build(), + "head" -> AttributeValue.builder.n("42").build() + ) + ) + } } } object ScynamoEncoderTest { + case class Typed[A](tpe: String, value: A) + object Typed { + implicit def encoder[A: ObjectScynamoEncoder]: ObjectScynamoEncoder[Typed[A]] = + ObjectScynamoEncoder.instance { case Typed(tpe, value) => + for { + encodedType <- tpe.encoded + encodedValue <- value.encodedMap + } yield { + encodedValue.put("type", encodedType) + encodedValue + } + } + } + case class Snake[A](head: A, tail: Option[Snake[A]]) object Snake { - implicit def codec[A: ScynamoCodec]: ObjectScynamoCodec[Snake[A]] = - scynamo.generic.semiauto.deriveScynamoCodec[Snake[A]] + implicit def codec[A: ScynamoEncoder]: ObjectScynamoEncoder[Snake[A]] = + scynamo.generic.semiauto.deriveScynamoEncoder[Snake[A]] } case class Foo(i: Int)