From 61c9450c4f0dce1f4e6afa8eca3054f6a1265149 Mon Sep 17 00:00:00 2001 From: Krzysztof Ciesielski Date: Wed, 19 Jun 2024 14:33:16 +0200 Subject: [PATCH] Better error message for jsoniter (#3860) --- .../sttp/tapir/json/jsoniter/TapirJsonJsoniter.scala | 8 ++++++-- .../tapir/json/jsoniter/TapirJsonJsoniterTests.scala | 11 +++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/json/jsoniter/src/main/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniter.scala b/json/jsoniter/src/main/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniter.scala index d4ef07306c..954e5586dc 100644 --- a/json/jsoniter/src/main/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniter.scala +++ b/json/jsoniter/src/main/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniter.scala @@ -2,13 +2,14 @@ package sttp.tapir.json.jsoniter import com.github.plokhotnyuk.jsoniter_scala.core._ import sttp.tapir.Codec.JsonCodec -import sttp.tapir.DecodeResult.Error.JsonDecodeException +import sttp.tapir.DecodeResult.Error.{JsonDecodeException, JsonError} import sttp.tapir.DecodeResult.{Error, Value} import sttp.tapir._ import scala.util.{Failure, Success, Try} trait TapirJsonJsoniter { + lazy val readerConfig = ReaderConfig.withAppendHexDumpToParseException(false) def jsonBody[T: JsonValueCodec: Schema]: EndpointIO.Body[String, T] = stringBodyUtf8AnyFormat(jsoniterCodec[T]) def jsonBodyWithRaw[T: JsonValueCodec: Schema]: EndpointIO.Body[String, (String, T)] = stringBodyUtf8AnyFormat( @@ -20,7 +21,10 @@ trait TapirJsonJsoniter { implicit def jsoniterCodec[T: JsonValueCodec: Schema]: JsonCodec[T] = sttp.tapir.Codec.json { s => - Try(readFromString[T](s)) match { + Try(readFromString[T](s, readerConfig)) match { + case Failure(error: JsonReaderException) => + val errMsg = Option(error.getMessage) + Error(s, JsonDecodeException(errors = errMsg.toList.map(e => JsonError(e, Nil)), error)) case Failure(error) => Error(s, JsonDecodeException(errors = List.empty, error)) case Success(v) => Value(v) } diff --git a/json/jsoniter/src/test/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniterTests.scala b/json/jsoniter/src/test/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniterTests.scala index 65379b34bb..9953cb749d 100644 --- a/json/jsoniter/src/test/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniterTests.scala +++ b/json/jsoniter/src/test/scala/sttp/tapir/json/jsoniter/TapirJsonJsoniterTests.scala @@ -5,10 +5,13 @@ import com.github.plokhotnyuk.jsoniter_scala.macros._ import org.scalatest.flatspec.AnyFlatSpecLike import org.scalatest.matchers.should.Matchers import sttp.tapir.DecodeResult -import sttp.tapir.DecodeResult.Error.JsonDecodeException +import sttp.tapir.DecodeResult.Error.{JsonDecodeException, JsonError} import sttp.tapir.generic.auto._ object TapirJsoniterCodec extends TapirJsonJsoniter +object TapirJsoniterCustomCodec extends TapirJsonJsoniter { + override lazy val readerConfig: ReaderConfig = ReaderConfig.withAppendHexDumpToParseException(true) +} class TapirJsonJsoniterTests extends AnyFlatSpecLike with Matchers { @@ -17,6 +20,7 @@ class TapirJsonJsoniterTests extends AnyFlatSpecLike with Matchers { it should "return a JSON specific decode error on failure" in { implicit val codec: JsonValueCodec[Customer] = JsonCodecMaker.make val tapirCodec = TapirJsoniterCodec.jsoniterCodec[Customer] + val tapirCodecWithHexDump = TapirJsoniterCustomCodec.jsoniterCodec[Customer] val actual = tapirCodec.decode("{}") actual shouldBe a[DecodeResult.Error] @@ -26,7 +30,10 @@ class TapirJsonJsoniterTests extends AnyFlatSpecLike with Matchers { failure.error shouldBe a[JsonDecodeException] val error = failure.error.asInstanceOf[JsonDecodeException] - error.errors shouldEqual List.empty + error.errors shouldEqual List(JsonError("missing required field \"name\", offset: 0x00000001", Nil)) + tapirCodecWithHexDump.decode("{}") should matchPattern { + case DecodeResult.Error(_, JsonDecodeException(errs, _: JsonReaderException)) if errs.head.msg.contains("buf:") => + } error.underlying shouldBe a[JsonReaderException] }