I have a simple code which using tapir
sttp.client3.json._
:
def method(...): Task[MyResponse] =
someLogic().response(asJson[MyResponse]).flatMap(res => ZIO.fromEither(res.body))
But now I want to add here some logic to use custom errors when asJson[MyResponse]
failed.
I created my error hierarchy:
sealed trait MyError extends Throwable
case class MyFirstError (msg: String) extends MyError
case class MySecondError (msg: String) extends MyError
And I changed Task
to IO[MyError, MyResponse]
, but I have no idea how should I return errors when they appeared and return same correct MyResponse
when everything is ok.
I tried smth like this:
def method(...): IO[MyError, MyResponse] =
someLogic().response(asJson[MyResponse]).flatMap{ res =>
res.code match {
case StatusCode.BadRequest => ZIO.fail(MyFristError("my error"))
case StatusCode.Forbidden => ZIO.fail(MySecondError("my other error"))
case _ => ZIO.fromEither(res.body).orDie
}
}
I got an error here Required IO[MyError, MyResponse], Found ZIO[Any, Throwable, MyResponse]
How should I fix this code to return my custom errors depend on the status code and correct response when everything is ok?
CodePudding user response:
I think you're running into this issue on the "catch-all" case:
case _ => ZIO.fromEither(res.body).orDie
The fromEither
is returning some IO[E, A]
my assumption is that the E
is where you're running into the Throwable
. Explicity declaring the types should fix it: fromEither[MyError, MyResponse]
.
If adding the types explicity isn't possible, then instead of using orDie
, you could use refineOrDie { case e: MyError => e }
.
CodePudding user response:
The signature defines what kind of errors your method can produce. orDie
terminates the fiber (with any Throwable
, not necessarily MyError
) so you can
- either catchall all exceptions and map to your trait:
sealed trait MyError extends Throwable
case class MyFirstError (msg: String) extends MyError
case class MySecondError (msg: String) extends MyError
case class UnexpectedResponseCode(msg: String) extends MyError
def method(...): IO[MyError, MyResponse] =
someLogic().response(asJson[MyResponse]).flatMap{ res =>
res.code match {
case StatusCode.BadRequest => ZIO.fail(MyFristError("my error"))
case StatusCode.Forbidden => ZIO.fail(MySecondError("my other error"))
case unexpectedResponseCode => ZIO.fail(UnexpectedResponseCode(s"I.I. Rabi> Who ordered that '$unexpectedResponseCode'?")
}
}
}
- Or make the signature more general, i.e.
def method(...): Task[MyResponse] // AKA IO[Throwable, MyResponse]