diff --git a/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ArgonautSupportSpec.scala b/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ArgonautSupportSpec.scala index b2cf1245..1fa47f11 100644 --- a/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ArgonautSupportSpec.scala +++ b/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ArgonautSupportSpec.scala @@ -107,7 +107,7 @@ final class ArgonautSupportSpec extends AsyncWordSpec with Matchers with BeforeA val `application/json-home` = MediaType.applicationWithFixedCharset("json-home", HttpCharsets.`UTF-8`, "json-home") - final object CustomArgonautSupport extends ArgonautSupport { + object CustomArgonautSupport extends ArgonautSupport { override def unmarshallerContentTypes = List(`application/json`, `application/json-home`) } import CustomArgonautSupport._ diff --git a/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ExampleApp.scala b/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ExampleApp.scala index e2ed6f4b..a31abe95 100644 --- a/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ExampleApp.scala +++ b/akka-http-argonaut/src/test/scala/de/heikoseeberger/akkahttpargonaut/ExampleApp.scala @@ -32,7 +32,7 @@ import scala.io.StdIn object ExampleApp { - final object Foo { + object Foo { implicit val fooCodec: CodecJson[Foo] = casecodec1(Foo.apply, (f: Foo) => Option(f.bar))("bar") } diff --git a/akka-http-circe/src/main/scala/de/heikoseeberger/akkahttpcirce/CirceSupport.scala b/akka-http-circe/src/main/scala/de/heikoseeberger/akkahttpcirce/CirceSupport.scala index a0e0e771..875f4ba9 100644 --- a/akka-http-circe/src/main/scala/de/heikoseeberger/akkahttpcirce/CirceSupport.scala +++ b/akka-http-circe/src/main/scala/de/heikoseeberger/akkahttpcirce/CirceSupport.scala @@ -64,7 +64,7 @@ trait FailFastCirceSupport extends BaseCirceSupport with FailFastUnmarshaller */ object ErrorAccumulatingCirceSupport extends ErrorAccumulatingCirceSupport { final case class DecodingFailures(failures: NonEmptyList[DecodingFailure]) extends Exception { - override def getMessage = failures.toList.map(_.show).mkString("\n") + override def getMessage: String = failures.toList.map(_.show).mkString("\n") } } diff --git a/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/CirceSupportSpec.scala b/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/CirceSupportSpec.scala index 2171cd54..75423908 100644 --- a/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/CirceSupportSpec.scala +++ b/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/CirceSupportSpec.scala @@ -18,25 +18,20 @@ package de.heikoseeberger.akkahttpcirce import akka.actor.ActorSystem import akka.http.scaladsl.marshalling.Marshal -import akka.http.scaladsl.model.{ - ContentTypeRange, - HttpCharsets, - HttpEntity, - MediaType, - RequestEntity, - ResponseEntity -} import akka.http.scaladsl.model.ContentTypes.{ `application/json`, `text/plain(UTF-8)` } -import akka.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } +import akka.http.scaladsl.model._ import akka.http.scaladsl.unmarshalling.Unmarshaller.UnsupportedContentTypeException +import akka.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } import akka.stream.scaladsl.{ Sink, Source } import cats.data.{ NonEmptyList, ValidatedNel } -import io.circe.{ DecodingFailure, Encoder, ParsingFailure, Printer } +import cats.implicits.toShow import io.circe.CursorOp.DownField -import org.scalatest.{ BeforeAndAfterAll, EitherValues } +import io.circe.{ DecodingFailure, ParsingFailure, Printer } import org.scalatest.concurrent.ScalaFutures import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AsyncWordSpec +import org.scalatest.{ BeforeAndAfterAll, EitherValues } + import scala.concurrent.Await import scala.concurrent.duration.DurationInt @@ -67,7 +62,7 @@ final class CirceSupportSpec /** * Specs common to both [[FailFastCirceSupport]] and [[ErrorAccumulatingCirceSupport]] */ - private def commonCirceSupport(support: BaseCirceSupport) = { + private def commonCirceSupport(support: BaseCirceSupport): Unit = { import io.circe.generic.auto._ import support._ @@ -79,24 +74,6 @@ final class CirceSupportSpec .map(_ shouldBe foo) } - "enable streamed marshalling and unmarshalling for json arrays" in { - val foos = (0 to 100).map(i => Foo(s"bar-$i")).toList - - // Don't know why, the encoder is not resolving alongside the marshaller - // this only happens if we use the implicits from BaseCirceSupport - // so, tried to create it before and guess what? it worked. - // not sure if this is a bug, but, the error is this: - // diverging implicit expansion for type io.circe.Encoder[A] - // [error] starting with lazy value encodeZoneOffset in object Encoder - implicit val e = implicitly[Encoder[Foo]] - - Marshal(Source(foos)) - .to[ResponseEntity] - .flatMap(entity => Unmarshal(entity).to[SourceOf[Foo]]) - .flatMap(_.runWith(Sink.seq)) - .map(_ shouldBe foos) - } - "provide proper error messages for requirement errors" in { val entity = HttpEntity(`application/json`, """{ "bar": "baz" }""") Unmarshal(entity) @@ -145,6 +122,15 @@ final class CirceSupportSpec behave like commonCirceSupport(FailFastCirceSupport) + "enable streamed marshalling and unmarshalling for json arrays" in { + val foos = (0 to 100).map(i => Foo(s"bar-$i")).toList + Marshal(Source(foos)) + .to[ResponseEntity] + .flatMap(entity => Unmarshal(entity).to[SourceOf[Foo]]) + .flatMap(_.runWith(Sink.seq)) + .map(_ shouldBe foos) + } + "fail with a ParsingFailure when unmarshalling empty entities with safeUnmarshaller" in { val entity = HttpEntity.empty(`application/json`) Unmarshal(entity) @@ -156,27 +142,30 @@ final class CirceSupportSpec "fail-fast and return only the first unmarshalling error" in { val entity = HttpEntity(`application/json`, """{ "a": 1, "b": 2 }""") - val error = DecodingFailure("String", List(DownField("a"))) + val error = + DecodingFailure("Got value '1' with wrong type, expecting string", List(DownField("a"))) Unmarshal(entity) .to[MultiFoo] .failed - .map(_ shouldBe error) + .map(_.getMessage shouldBe error.getMessage()) } "fail-fast and return only the first unmarshalling error with safeUnmarshaller" in { val entity = HttpEntity(`application/json`, """{ "a": 1, "b": 2 }""") - val error = DecodingFailure("String", List(DownField("a"))) + val error: io.circe.Error = + DecodingFailure("Got value '1' with wrong type, expecting string", List(DownField("a"))) Unmarshal(entity) .to[Either[io.circe.Error, MultiFoo]] .futureValue .left - .value shouldBe error + .value + .getMessage shouldBe error.getMessage } "allow unmarshalling with passed in Content-Types" in { val foo = Foo("bar") - final object CustomCirceSupport extends FailFastCirceSupport { + object CustomCirceSupport extends FailFastCirceSupport { override def unmarshallerContentTypes: List[ContentTypeRange] = List(`application/json`, `application/json-home`) } @@ -193,6 +182,16 @@ final class CirceSupportSpec behave like commonCirceSupport(ErrorAccumulatingCirceSupport) + "enable streamed marshalling and unmarshalling for json arrays" in { + val foos = (0 to 100).map(i => Foo(s"bar-$i")).toList + Marshal(Source(foos)) + .to[ResponseEntity] + .flatMap(entity => Unmarshal(entity).to[SourceOf[Foo]]) + .flatMap(_.runWith(Sink.seq)) + .map(_ shouldBe foos) + + } + "fail with a NonEmptyList of Errors when unmarshalling empty entities with safeUnmarshaller" in { val entity = HttpEntity.empty(`application/json`) Unmarshal(entity) @@ -207,8 +206,8 @@ final class CirceSupportSpec val entity = HttpEntity(`application/json`, """{ "a": 1, "b": 2 }""") val errors = NonEmptyList.of( - DecodingFailure("String", List(DownField("a"))), - DecodingFailure("String", List(DownField("b"))) + DecodingFailure("Got value '1' with wrong type, expecting string", List(DownField("a"))), + DecodingFailure("Got value '2' with wrong type, expecting string", List(DownField("b"))) ) val errorMessage = ErrorAccumulatingCirceSupport.DecodingFailures(errors).getMessage Unmarshal(entity) @@ -219,24 +218,31 @@ final class CirceSupportSpec "accumulate and return all unmarshalling errors with safeUnmarshaller" in { val entity = HttpEntity(`application/json`, """{ "a": 1, "b": 2 }""") - val errors = + val errors: NonEmptyList[DecodingFailure] = NonEmptyList.of( - DecodingFailure("String", List(DownField("a"))), - DecodingFailure("String", List(DownField("b"))) + DecodingFailure("Got value '1' with wrong type, expecting string", List(DownField("a"))), + DecodingFailure("Got value '2' with wrong type, expecting string", List(DownField("b"))) ) val errorMessage = ErrorAccumulatingCirceSupport.DecodingFailures(errors).getMessage - Unmarshal(entity) + + val result: String = Unmarshal(entity) .to[ValidatedNel[io.circe.Error, MultiFoo]] .futureValue .toEither .left - .value shouldBe errors + .value + .collect { case df: DecodingFailure => + df.show + } + .mkString("\n") + + errorMessage shouldBe result } "allow unmarshalling with passed in Content-Types" in { val foo = Foo("bar") - final object CustomCirceSupport extends ErrorAccumulatingCirceSupport { + object CustomCirceSupport extends ErrorAccumulatingCirceSupport { override def unmarshallerContentTypes: List[ContentTypeRange] = List(`application/json`, `application/json-home`) } diff --git a/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/ExampleApp.scala b/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/ExampleApp.scala index 2045d8d0..b085f78a 100644 --- a/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/ExampleApp.scala +++ b/akka-http-circe/src/test/scala/de/heikoseeberger/akkahttpcirce/ExampleApp.scala @@ -54,7 +54,7 @@ object ExampleApp { } } ~ pathPrefix("stream") { post { - entity(as[SourceOf[Foo]]) { fooSource: SourceOf[Foo] => + entity(as[SourceOf[Foo]]) { (fooSource: SourceOf[Foo]) => import sys._ Marshal(Source.single(Foo("a"))).to[RequestEntity] diff --git a/akka-http-json4s/src/main/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupport.scala b/akka-http-json4s/src/main/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupport.scala index 3777e8f1..5a0c4e66 100644 --- a/akka-http-json4s/src/main/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupport.scala +++ b/akka-http-json4s/src/main/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupport.scala @@ -43,9 +43,9 @@ object Json4sSupport extends Json4sSupport { sealed abstract class ShouldWritePretty - final object ShouldWritePretty { - final object True extends ShouldWritePretty - final object False extends ShouldWritePretty + object ShouldWritePretty { + object True extends ShouldWritePretty + object False extends ShouldWritePretty } } diff --git a/akka-http-json4s/src/test/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupportSpec.scala b/akka-http-json4s/src/test/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupportSpec.scala index ab7d0df9..272463df 100644 --- a/akka-http-json4s/src/test/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupportSpec.scala +++ b/akka-http-json4s/src/test/scala/de/heikoseeberger/akkahttpjson4s/Json4sSupportSpec.scala @@ -117,7 +117,7 @@ final class Json4sSupportSpec extends AsyncWordSpec with Matchers with BeforeAnd val `application/json-home` = MediaType.applicationWithFixedCharset("json-home", HttpCharsets.`UTF-8`, "json-home") - final object CustomJson4sSupport extends Json4sSupport { + object CustomJson4sSupport extends Json4sSupport { override def unmarshallerContentTypes = List(`application/json`, `application/json-home`) } import CustomJson4sSupport._ diff --git a/akka-http-jsoniter-scala/src/test/scala/de/heikoseeberger/akkahttpjsoniterscala/JsoniterScalaSupportSpec.scala b/akka-http-jsoniter-scala/src/test/scala/de/heikoseeberger/akkahttpjsoniterscala/JsoniterScalaSupportSpec.scala index f7b82cea..b777f937 100644 --- a/akka-http-jsoniter-scala/src/test/scala/de/heikoseeberger/akkahttpjsoniterscala/JsoniterScalaSupportSpec.scala +++ b/akka-http-jsoniter-scala/src/test/scala/de/heikoseeberger/akkahttpjsoniterscala/JsoniterScalaSupportSpec.scala @@ -95,7 +95,7 @@ final class JsoniterScalaSupportSpec extends AsyncWordSpec with Matchers with Be val `application/json-home` = MediaType.applicationWithFixedCharset("json-home", HttpCharsets.`UTF-8`, "json-home") - final object CustomJsoniterScalaSupport extends JsoniterScalaSupport { + object CustomJsoniterScalaSupport extends JsoniterScalaSupport { override def unmarshallerContentTypes: List[ContentTypeRange] = List(`application/json`, `application/json-home`) } diff --git a/akka-http-zio-json/src/main/scala/de/heikoseeberger/akkahttpziojson/ZioJsonSupport.scala b/akka-http-zio-json/src/main/scala/de/heikoseeberger/akkahttpziojson/ZioJsonSupport.scala index 9a385410..6268487a 100644 --- a/akka-http-zio-json/src/main/scala/de/heikoseeberger/akkahttpziojson/ZioJsonSupport.scala +++ b/akka-http-zio-json/src/main/scala/de/heikoseeberger/akkahttpziojson/ZioJsonSupport.scala @@ -102,7 +102,7 @@ trait ZioJsonSupport { Unmarshaller(_ => bs => { val decoded = jd.decodeJsonStreamInput(ZStream.fromIterable(bs)) - Unsafe.unsafeCompat(implicit u => rt.unsafe.runToFuture(decoded)) + Unsafe.unsafe(implicit u => rt.unsafe.runToFuture(decoded)) } ) diff --git a/build.sbt b/build.sbt index a16eee7d..78e7d1e6 100644 --- a/build.sbt +++ b/build.sbt @@ -40,7 +40,7 @@ inThisBuild( ) val withScala3 = Seq( - crossScalaVersions += "3.2.1", + crossScalaVersions += "3.3.1", ) // ***************************************************************************** @@ -86,7 +86,7 @@ lazy val `akka-http-argonaut` = lazy val `akka-http-circe` = project .enablePlugins(AutomateHeaderPlugin) - .settings(commonSettings) + .settings(commonSettings, withScala3) .settings( libraryDependencies ++= Seq( library.akkaHttp, @@ -227,11 +227,11 @@ lazy val commonSettings = lazy val library = new { object Version { - val akka = "2.6.20" - val akkaHttp = "10.2.10" + val akka = "2.8.5" + val akkaHttp = "10.5.3" val argonaut = "6.3.8" val avro4s = "4.0.12" - val circe = "0.14.1" + val circe = "0.14.6" val jacksonModuleScala = "2.13.1" val json4s = "4.0.6" val jsoniterScala = "2.17.9"