I'm using http4s & rho (mainly for Swagger integration)
My services are using this DAO object, that methods that can throw Exceptions (fail the Task
)
case class BasicMatchDao() {
def readAll(): Task[List[BasicMatch]] = Task.fail(ActionNotImplemented("readAll"))
def read(id: String): Task[Option[BasicMatch]] = readQuery(id).option.transact(xa)
}
In my RhoService I can handle these like
private def exceptionToJson(t: Throwable):Json = Json.obj("error" -> t.getMessage.asJson)
val rhoService = new RhoService {
GET / path |>> { (request: Request) =>
Ok(dao.readAll.map(_.asJson)).handleWith {
case t:ActionNotImplemented => NotImplemented(exceptionToJson(t))
case t:Throwable => InternalServerError(exceptionToJson(t))
}
}
This way I make sure that whatever I return, it's always a Json
Since I don't want to pollute every RhoRoute with a similar errorhandling I want to do something which is possible with the default http4s.dsl, but I can't seem to get working with rho:
1. Create default error handler
e.g. add
...
Ok(dao.readAll.map(_.asJson)).handleWith(errorHandler)
...
private def errorHandler(): PartialFunction[Throwable, Task[Response]] = {
case t:ActionNotImplemented => NotImplemented(exceptionToJson(t))
case t:Throwable => InternalServerError(exceptionToJson(t))
}
This will fail because NotImplemented is not a Response (I can call .pure on these to make type checking work) But then the code will compile, but I get this exception:
Cannot convert from fs2.Task[Product with Serializable] to an Entity, because no EntityEncoder[fs2.Task[Product with Serializable]] instance could be found. Ok(dao.readAll.map(_.asJson)).handleWith(errorHandler)
2. Add errorhandler to each RhoRoute
After defining the rhoRoute I'd like to map over it and add the errorhandler to each route, so do something at the r that let's me add the 'handleWith' somewhere (below will not work)
new RhoService(rhoService.getRoutes.map(_.handleWith(errorHandler))
If I can't get this to work, I'll probably move back to the default dsl, but I really liked rho
So Part 1 is fixed for now. Defining the Task as Task[BaseResult]
instead of Task[Response]
will work
import org.http4s.rho.Result.BaseResult
val errorHandler: PartialFunction[Throwable, Task[BaseResult]] = {
case t:ActionNotImplemented => NotImplemented(exceptionToJson(t))
case t:Throwable => InternalServerError(exceptionToJson(t))
}
I'm looking into part 2 as well. All help is welcome :-)