diff --git a/build.sbt b/build.sbt index e8c7d07c..1d845c23 100644 --- a/build.sbt +++ b/build.sbt @@ -33,7 +33,7 @@ addCommandAlias("check", "all scalafmtSbtCheck scalafmtCheck test:scalafmtCheck" val zioVersion = "2.1.1" val zioAwsVersion = "7.21.15.12" -val zioSchemaVersion = "0.4.17" +val zioSchemaVersion = "1.2.1" val zioPreludeVersion = "1.0.0-RC25" val zioInteropCats3Version = "23.1.0.2" val catsEffect3Version = "3.5.4" diff --git a/dynamodb/src/main/scala/zio/dynamodb/Codec.scala b/dynamodb/src/main/scala/zio/dynamodb/Codec.scala index 08b23a29..db4f979a 100644 --- a/dynamodb/src/main/scala/zio/dynamodb/Codec.scala +++ b/dynamodb/src/main/scala/zio/dynamodb/Codec.scala @@ -214,7 +214,7 @@ private[dynamodb] object Codec { case StandardType.DoubleType => (a: A) => AttributeValue.Number(BigDecimal(a.toString)) case StandardType.BigDecimalType => (a: A) => AttributeValue.Number(BigDecimal(a.toString)) case StandardType.BigIntegerType => (a: A) => AttributeValue.Number(BigDecimal(a.toString)) - case StandardType.UUIDType => (a: A) => AttributeValue.String(a.toString) + case StandardType.CurrencyType => (a: A) => AttributeValue.String(a.toString) case StandardType.DayOfWeekType => (a: A) => AttributeValue.String(a.toString) case StandardType.DurationType => (a: A) => AttributeValue.String(a.toString) case StandardType.InstantType => (a: A) => AttributeValue.String(a.toString) @@ -226,6 +226,7 @@ private[dynamodb] object Codec { case StandardType.OffsetDateTimeType => (a: A) => AttributeValue.String(a.toString) case StandardType.OffsetTimeType => (a: A) => AttributeValue.String(a.toString) case StandardType.PeriodType => (a: A) => AttributeValue.String(a.toString) + case StandardType.UUIDType => (a: A) => AttributeValue.String(a.toString) case StandardType.YearType => yearEncoder case StandardType.YearMonthType => (a: A) => AttributeValue.String(a.toString) case StandardType.ZonedDateTimeType => (a: A) => AttributeValue.String(a.toString) @@ -611,6 +612,13 @@ private[dynamodb] object Codec { FromAttributeValue.stringFromAttributeValue.fromAttributeValue(av).flatMap { s => Try(UUID.fromString(s)).toEither.left.map(iae => DecodingError(s"Invalid UUID: ${iae.getMessage}")) } + case StandardType.CurrencyType => + (av: AttributeValue) => + FromAttributeValue.stringFromAttributeValue.fromAttributeValue(av).flatMap { s => + Try(java.util.Currency.getInstance(s)).toEither.left.map(e => + DecodingError(s"Invalid Currency: ${e.getMessage}") + ) + } case StandardType.DayOfWeekType => (av: AttributeValue) => javaTimeStringParser(av)(DayOfWeek.valueOf(_)) case StandardType.DurationType => diff --git a/dynamodb/src/test/scala/zio/dynamodb/codec/CodecTestFixtures.scala b/dynamodb/src/test/scala/zio/dynamodb/codec/CodecTestFixtures.scala index 62d5925e..0886aeb7 100644 --- a/dynamodb/src/test/scala/zio/dynamodb/codec/CodecTestFixtures.scala +++ b/dynamodb/src/test/scala/zio/dynamodb/codec/CodecTestFixtures.scala @@ -38,6 +38,7 @@ trait CodecTestFixtures { ]("boolean")(_.asInstanceOf[Boolean])(_.asInstanceOf[Any])(_.isInstanceOf[Boolean]) ) + lazy implicit val caseClassOfCurrencySchema: Schema[CaseClassOfCurrency] = DeriveSchema.gen[CaseClassOfCurrency] lazy implicit val nestedCaseClass2Schema: Schema[NestedCaseClass2] = DeriveSchema.gen[NestedCaseClass2] lazy implicit val simpleCaseClass3Schema: Schema[SimpleCaseClass3] = DeriveSchema.gen[SimpleCaseClass3] lazy implicit val simpleCaseClass3SchemaOptional: Schema[SimpleCaseClass3Option] = diff --git a/dynamodb/src/test/scala/zio/dynamodb/codec/DynamicValueGen.scala b/dynamodb/src/test/scala/zio/dynamodb/codec/DynamicValueGen.scala index 70c758aa..c7c78a56 100644 --- a/dynamodb/src/test/scala/zio/dynamodb/codec/DynamicValueGen.scala +++ b/dynamodb/src/test/scala/zio/dynamodb/codec/DynamicValueGen.scala @@ -5,6 +5,7 @@ import zio.test.{ Gen, Sized } import zio.Chunk import scala.collection.immutable.ListMap +import scala.jdk.CollectionConverters._ object DynamicValueGen { @@ -24,6 +25,8 @@ object DynamicValueGen { case typ: StandardType.FloatType.type => gen(typ, Gen.float) case typ: StandardType.BigDecimalType.type => gen(typ, Gen.double.map(d => java.math.BigDecimal.valueOf(d))) case typ: StandardType.BigIntegerType.type => gen(typ, Gen.long.map(n => java.math.BigInteger.valueOf(n))) + case typ: StandardType.CurrencyType.type => + gen(typ, Gen.fromIterable(java.util.Currency.getAvailableCurrencies.asScala)) case typ: StandardType.DayOfWeekType.type => gen(typ, JavaTimeGen.anyDayOfWeek) case typ: StandardType.DurationType.type => gen(typ, JavaTimeGen.anyDuration) case typ: StandardType.InstantType.type => gen(typ, JavaTimeGen.anyInstant) @@ -82,6 +85,7 @@ object DynamicValueGen { case Schema.Either(left, right, _) => Gen.oneOf(anyDynamicLeftValueOfSchema(left), anyDynamicRightValueOfSchema(right)) case Schema.Transform(schema, _, _, _, _) => anyDynamicValueOfSchema(schema) case Schema.Fail(message, _) => Gen.const(DynamicValue.Error(message)) + case Schema.Fallback(left, right, _, _) => Gen.oneOf(anyDynamicLeftValueOfSchema(left), anyDynamicRightValueOfSchema(right)) // TODO: Avi - check case l @ Schema.Lazy(_) => anyDynamicValueOfSchema(l.schema) } //scalafmt: { maxColumn = 120 } diff --git a/dynamodb/src/test/scala/zio/dynamodb/codec/ItemDecoderSpec.scala b/dynamodb/src/test/scala/zio/dynamodb/codec/ItemDecoderSpec.scala index 1eb046b9..93604ff0 100644 --- a/dynamodb/src/test/scala/zio/dynamodb/codec/ItemDecoderSpec.scala +++ b/dynamodb/src/test/scala/zio/dynamodb/codec/ItemDecoderSpec.scala @@ -29,6 +29,14 @@ object ItemDecoderSpec extends ZIOSpecDefault with CodecTestFixtures { assertTrue(actual == Right("FOO")) }, + test("decoded currency") { + val currency: java.util.Currency = java.util.Currency.getInstance("GBP") + val expected = CaseClassOfCurrency(currency) + + val actual = DynamoDBQuery.fromItem[CaseClassOfCurrency](Item("c" -> "GBP")) + + assert(actual)(isRight(equalTo(expected))) + }, test("decoded list") { val expected = CaseClassOfList(List(1, 2)) diff --git a/dynamodb/src/test/scala/zio/dynamodb/codec/ItemEncoderSpec.scala b/dynamodb/src/test/scala/zio/dynamodb/codec/ItemEncoderSpec.scala index 4d9ab1d9..d2d5fe0e 100644 --- a/dynamodb/src/test/scala/zio/dynamodb/codec/ItemEncoderSpec.scala +++ b/dynamodb/src/test/scala/zio/dynamodb/codec/ItemEncoderSpec.scala @@ -29,6 +29,15 @@ object ItemEncoderSpec extends ZIOSpecDefault with CodecTestFixtures { equalTo(AttributeValue.Map(Map(toAvString("string") -> toAvString("FOO")))) ) }, + test("encodes a Currency") { + val expectedItem: Item = Item("c" -> "GBP") + val currency: java.util.Currency = java.util.Currency.getInstance("GBP") + println(s"currency: $currency") + + val item = DynamoDBQuery.toItem(CaseClassOfCurrency(currency)) + + assert(item)(equalTo(expectedItem)) + }, test("encodes List of Int") { val expectedItem: Item = Item("nums" -> List(1, 2, 3)) diff --git a/dynamodb/src/test/scala/zio/dynamodb/codec/StandardTypeGen.scala b/dynamodb/src/test/scala/zio/dynamodb/codec/StandardTypeGen.scala index dd87dc23..b53acc52 100644 --- a/dynamodb/src/test/scala/zio/dynamodb/codec/StandardTypeGen.scala +++ b/dynamodb/src/test/scala/zio/dynamodb/codec/StandardTypeGen.scala @@ -5,6 +5,8 @@ import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt } import zio.test.{ Gen, Sized } import zio.schema.StandardType +import scala.jdk.CollectionConverters._ + object StandardTypeGen { def anyStandardType[A]: Gen[Any, StandardType[A]] = @@ -22,6 +24,7 @@ object StandardTypeGen { (StandardType.BigDecimalType), (StandardType.BigIntegerType), (StandardType.CharType), + (StandardType.CurrencyType), (StandardType.UUIDType), (StandardType.DayOfWeekType), (StandardType.DurationType), @@ -56,6 +59,9 @@ object StandardTypeGen { def anyStandardTypeAndGen[A]: Gen[Any, StandardTypeAndGen[A]] = anyStandardType[A].map { + case typ: StandardType.CurrencyType.type => + val allCurrencies: List[java.util.Currency] = java.util.Currency.getAvailableCurrencies.asScala.toList + (typ -> Gen.fromIterable(allCurrencies)).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.StringType.type => (typ -> Gen.string).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.BoolType.type => (typ -> Gen.boolean).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.ShortType.type => (typ -> Gen.short).asInstanceOf[StandardTypeAndGen[A]] @@ -64,6 +70,7 @@ object StandardTypeGen { case typ: StandardType.FloatType.type => (typ -> Gen.float).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.DoubleType.type => (typ -> Gen.double).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.BinaryType.type => (typ -> Gen.chunkOf(Gen.byte)).asInstanceOf[StandardTypeAndGen[A]] + case typ: StandardType.ByteType.type => (typ -> Gen.byte).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.CharType.type => (typ -> Gen.asciiChar).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.UUIDType.type => (typ -> Gen.uuid).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.BigDecimalType.type => (typ -> javaBigDecimal).asInstanceOf[StandardTypeAndGen[A]] @@ -82,6 +89,8 @@ object StandardTypeGen { case typ: StandardType.OffsetTimeType.type => (typ -> JavaTimeGen.anyOffsetTime).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.PeriodType.type => (typ -> JavaTimeGen.anyPeriod).asInstanceOf[StandardTypeAndGen[A]] + case typ: StandardType.UnitType.type => + (typ -> Gen.unit).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.YearType.type => (typ -> JavaTimeGen.anyYear).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.YearMonthType.type => (typ -> JavaTimeGen.anyYearMonth).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.ZonedDateTimeType.type => @@ -89,6 +98,5 @@ object StandardTypeGen { case typ: StandardType.ZoneIdType.type => (typ -> JavaTimeGen.anyZoneId).asInstanceOf[StandardTypeAndGen[A]] case typ: StandardType.ZoneOffsetType.type => (typ -> JavaTimeGen.anyZoneOffset).asInstanceOf[StandardTypeAndGen[A]] - case _ => (StandardType.UnitType -> Gen.unit).asInstanceOf[StandardTypeAndGen[A]] } } diff --git a/dynamodb/src/test/scala/zio/dynamodb/codec/models.scala b/dynamodb/src/test/scala/zio/dynamodb/codec/models.scala index e1bd3d0a..37eabaa5 100644 --- a/dynamodb/src/test/scala/zio/dynamodb/codec/models.scala +++ b/dynamodb/src/test/scala/zio/dynamodb/codec/models.scala @@ -14,6 +14,9 @@ final case class Ok(response: List[String]) extends Status final case class Failed(code: Int, reason: String, additionalExplanation: Option[String], remark: String = "oops") extends Status case object Pending extends Status + +final case class CaseClassOfCurrency(c: java.util.Currency) + final case class NestedCaseClass2(id: Int, nested: SimpleCaseClass3) final case class SimpleCaseClass3(id: Int, name: String, flag: Boolean)