When a scala recover should be used: - scala

I have a simple flow of orders from an controller to services back to the controller, and im trying to make sure I use future recovers in the right place and in general to cover exceptions properly.
controller action:
def getSiblings(): Action[JsValue] = Action.async(parse.json) { request =>
request.body.validate[Person] match {
case JsSuccess(person, _) =>
peopleService.getSiblings(person).map(res => Ok(Json.toJson(res))) recover {
case t: Throwable =>
logger.error("error running getSiblings: ", t)
InternalServerError
}
case JsError(errors) => Future(BadRequest(s"not a good person format ${errors.mkString}"))
}
}
peopleService:
class PeopleService #Inject() extends LazyLogging {
def getSiblings(personToGetSiblings: Person): Future[List[SiblingResults]] = {
// isSibling is a method of a person that returnes a future and can fail
Future.sequence(listOfPeople.map(person => person.isSibling(personToGetSiblings))) recover {
case e: Exception => {
throw new RuntimeException("fail to get siblings with error: ", e)
}
}
}
}
case class SiblingResults (person: Option[Person])
and a Person:
#Singleton
class PersonA #Inject() (configuration: Configuration, peopleApi: PeopleApi) extends Person {
def isSibling(personToMatch: Person): Future[SiblingResults] = {
val res = for {
// areSiblings returnes a Future[Boolean]
areThey <- peopleApi.areSiblings(personToMatch, personInstance) recover {
case ex: Exception => throw new Exception("PeopleApi failed")
}
} yield areThey
if (res) Some(personInstance) else None
}
val personInstance = this
...
}
what will be the right way to recover those futures?

Use Play's Action composition to handle any failures. This your code would be clean handling only the business logic without the extra plumbing stuff like exception handling etc. You let the exceptions bubble up to the controller and exception is handled finally by the ActionBuilder.
ActionBuilder
import play.api.libs.json.Json
import play.api.mvc.{ActionBuilder, Request, Result}
import play.api.mvc.Results.Status
import scala.concurrent.Future
/**
* Created by chlr on 12/2/16.
*/
object ErrRecoveryAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]) = {
block(request) recover errorHandler
}
def errorHandler: PartialFunction[Throwable, Result] = {
// you can apply a different statuscode other than 500 depending on the type of exception matched.
case th: Throwable =>
new Status(500).apply(Json.obj("error_message" -> th.getMessage, "error_class" -> th.getClass.getName))
}
}
Controller usage:
Note how there is no exception handling in the controller and furthermore no exception handling is required in other service classes.
def getSiblings(): Action[JsValue] = ErrRecoveryAction.async(parse.json) {
request =>
peopleService
.getSiblings(request.body.as[Person])
.map(res => Ok(Json.toJson(res)))
}

Related

Need help understanding return type for Akka Typed ask

I am trying to get "ask" working for Akka Typed. I have followed examples online, and I thought I pretty much replicated what they showed, but I'm getting an compiler error when I try to evaluate the response from the "ask". Here's my minimal reproducible example.
SuperSimpleAsker is an actor that is requesting a "widget" from the MyWidgetKeeper actor. The response is a string representing the widget's id. All I'm trying to do so far is log the received widget id as a "Success" message, and will add more stuff to do with the id later. When the SuperSimpleAsker is created, the ActorRef of the MyWidgetKeeper is passed in. I have left out the Main program that creates the actors to keep the code simple.
The error that I get is:
type mismatch;
found : Unit
required: widgets.SuperSimpleAsker.Request
This error occurs on both of the logger.* lines (inside of the Case Failure and Case Success blocks toward the end of the code listing).
I don't understand what part of the code is requiring a "widgets.SuperSimpleAsker.Request" object or why.
package widgets
import scala.concurrent.duration.DurationInt
import scala.util.{Failure, Success}
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, Behavior}
import akka.util.Timeout
import com.typesafe.scalalogging.LazyLogging
object MyWidgetKeeper {
sealed trait Request
case class GetWidget(replyTo: ActorRef[Response]) extends Request
sealed trait Response
case class WidgetResponse(widget: String) extends Response
def apply(): Behavior[Request] =
new MyWidgetKeeper().myWidgetKeeper()
}
class MyWidgetKeeper {
import MyWidgetKeeper._
def myWidgetKeeper(): Behavior[Request] = {
Behaviors.receive { (context, message) =>
message match {
case GetWidget(replyTo) =>
replyTo ! WidgetResponse("12345")
Behaviors.same
}
}
}
}
object SuperSimpleAsker {
sealed trait Request
case object DoStuff extends Request
def apply(widgetKeeper: ActorRef[MyWidgetKeeper.Request]): Behavior[Request] =
new SuperSimpleAsker(widgetKeeper).simpleAsker()
}
class SuperSimpleAsker(widgetKeeper: ActorRef[MyWidgetKeeper.Request]) extends LazyLogging{
import SuperSimpleAsker._
import widgets.MyWidgetKeeper.GetWidget
private def simpleAsker(): Behavior[Request] = {
Behaviors.receive { (context, message) =>
message match {
case DoStuff =>
logger.info(f"Doing stuff")
implicit val timeout = Timeout(2000 millis)
context.ask(widgetKeeper, GetWidget)
{
case Failure(exception) =>
logger.error(f"Failed: ${exception.getMessage}")
case Success(response: MyWidgetKeeper.Response) =>
response match {
case MyWidgetKeeper.WidgetResponse(id) =>
logger.info(f"Success: Got Widget# $id")
// Do some more stuff with the widget id
}
}
Behaviors.same
}
}
}
}
In Akka Typed's context.ask, the passed function converts the successful or failed ask into a message which gets sent to the actor, ideally without performing a side effect.
So your SuperSimpleAsker will have to add messages that the ask can be converted to:
object SuperSimpleAsker {
sealed trait Request
case object DoStuff extends Request
case class WidgetResponseFor(widgetId: String) extends Request
case object NoWidgetResponse extends Request
def apply(widgetKeeper: ActorRef[MyWidgetKeeper.Request]): Behavior[Request] =
new SuperSimpleAsker(widgetKeeper).simpleAsker()
}
class SuperSimpleAsker(widgetKeeper: ActorRef[MyWidgetKeeper.Request]) extends LazyLogging{
import SuperSimpleAsker._
import widgets.MyWidgetKeeper.GetWidget
private def simpleAsker(): Behavior[Request] = {
Behaviors.receive { (context, message) =>
message match {
case DoStuff =>
logger.info(f"Doing stuff")
implicit val timeout = Timeout(2000 millis)
context.ask(widgetKeeper, GetWidget)
{
case Failure(_) => // there's actually only one possible exception: timed out
NoWidgetResponse
case Success(response: MyWidgetKeeper.Response) =>
WidgetResponseFor(response.widget)
}
Behaviors.same
case WidgetResponseFor(id) =>
logger.info(f"Success: Got Widget# $id")
// Do stuff with the widget id
Behaviors.same
case NoWidgetResponse =>
logger.error("Failed")
Behaviors.same
}
}
}
}

scalatest - test a method of Future[S] with fallbackTo

Premise: When my API responds to a request for the User object, I want to try enriching it with the properties of case class PartnerView(id: String, vipStatus: Option[Boolean], latestSession: Option[Timestamp]. Since the database can be unreliable at times, I use fallbackTo to provide the values as optional, thus not displaying them in the User JSON response.
The following implementation seems to work so far (running the request through Postman returns the User JSON without the optional values) yet my unit test would complain as if I had an uncaught Exception.
The Service class:
class Service(repo: Repository) {
def get(id: String): Future[Partner] = {
val account = repo.getAccount(id)
val getLatestSession = repo.getLatestSession(id)
val partnerView = (for {
account <- getAccount
latestStartTime <- getLatestSession.map {
case Some(x) => x.scheduledStartTime
case _ => None
}
} yield PartnerView(partnerId, account.vipStatus, latestStartTime))
.fallbackTo(Future.successful(PartnerView(id, None, None)))
partnerView
}
}
The Repository class:
class Repository(database: DatabaseDef, logger: LoggingAdapter) {
def getAccount(id: String): Future[Account] = database.run((...).result.head)
.recover {
case e: Exception =>
logger.error(e, "DB Server went down")
throw e
}
def getLatestSession(id: String): Future[Option[Session]] = database.run((...).result.headOption)
.recover {
case e: Exception =>
logger.error(e, "DB Server went down")
throw e
}
}
The Unit Test:
class ServiceSpec extends AsyncFlatSpec with AsyncMockFactory with OneInstancePerTest {
val mockRepo = mock[Repository]
val service = new Service(mockRepo)
behaviour of "Service"
it should "get an empty PartnerView when the repository get an Exception" in {
(mockRepository.getAccount _)
.expects("partner")
.throwing(new Exception)
service.get("partner")
.map(partnerView => assert(partnerView.id == "partner" && partnerView.vipStatus.isEmpty))
}
}
The test would fail with the message
Testing started at 5:15 p.m. ...
java.lang.Exception was thrown.
{stacktrace here}
I'm expecting the Exception to
By changing the mock setup to below, the test ran successfully:
it should "get an empty PartnerView when the repository get an Exception" in {
(mockRepository.getAccount _)
.expects("partner")
.returning(Future.failed(new Exception))
...
}
since the recover method wraps the Exception inside a Future
Sources:
recover vs recoverWith
official scala article

How to refactor this scala code?

I want to refactor this code into something more readable and better in general. I know that in Scala there are normally neat ways of doing things but for me it's getting a bit messy (BTW I'm using the Play library in the code). this is a snippet of my code:
class HomeController #Inject()
(cc: ControllerComponents)
(implicit val config: Configuration)
extends AbstractController(cc) {
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {
implicit request: Request[String] =>
val promise = Promise[Result]()
Future {
val removeId = request.body.toLong
println(s"remove id $removeId")
promise completeWith {
idElementsDAO.remove(removeId, uid.toLong) map {
_ => Ok("")
} recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
BadRequest(errorMessage)
}
}
} recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
promise.success(BadRequest(errorMessage))
}
promise.future
}
}
Assuming that idElementsDAO.remove return a Future, this is probably more idiomatic:
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {implicit request =>
val removeId = request.body.toLong
println(s"remove id $removeId")
idElementsDAO.remove(removeId, uid.toLong)
.map(_ => NoContent) // probably more correct than `Ok("")`
.recover {
case t: Throwable =>
val errorMessage: String = getMessageFromThrowable(t)
println("remove id element failure " + errorMessage)
BadRequest(errorMessage)
}
}
No need for the Promise or the call to Future {...} (Future.apply).
Keep in mind, it's probably not the best idea to directly pass the underlying error of any Throwable directly to the http client (browser?).
If you add generic error handling code to the global error handler (for unexpected errors) that logs the error and sends a generic message to the front-end, you can then write it even cleaner like this:
def removeIdElement(uid: String) =
HAction(uid, "AuthEvent", 1, "login", parse.text).async {implicit request =>
val removeId = request.body.toLong
println(s"remove id $removeId")
for {
_ <- idElementsDAO.remove(removeId, uid.toLong)
} yield NoContent
}
https://www.playframework.com/documentation/2.6.x/ScalaErrorHandling
Here is a simpler version of your code:
class HomeController #Inject()(cc: ControllerComponents)(implicit val config: Configuration)
extends AbstractController(cc) {
def removeIdElement(uid: String) = HAction(uid, "AuthEvent", 1, "login", parse.text).async {
implicit request: Request[String] =>
Future {
val removeId = request.body.toLong
println(s"Removing id $removeId")
removeId
}.flatMap(id => idElementsDAO.remove(id, uid.toLong))
.map(_ => Ok(""))
.recover {
case t: Throwable =>
val errorMessage = getMessageFromThrowable(t)
println(s"Removing id element failed: ${errorMessage}")
BadRequest(errorMessage)
}
}
}
In the above code, a Promise is not needed, and the recover combinator is not repeated.

in Scala, execute a block and ignore but log exceptions

Apache has IOUtils.closeQuietly(Closeable). In Scala, I'd like to generalize this: execute a block and ingore exceptions, while at the same time logging them. Something like this:
import LogUtils._
object Playground extends App {
implicit val logger_ = LoggerFactory.getLogger(getClass)
silentLog {
println("block")
throw new Exception("an exception")
}
println("end")
}
import org.slf4j.{Logger, LoggerFactory}
import scala.util.control.NonFatal
object LogUtils {
def silentLog[U](f: => U)(implicit log: Logger) {
try f
catch {
case NonFatal(e) => log.error(null, e)
}
}
}
Is this already implemented in some common library?
Try[T] does this to some extent but does not log the exception. Try does try .. catch ... inside
def LoggingTry[T](f: => T)(implicit logger: Logger): Try[T] = Try(f()).recoverWith {
case th =>
logger.log(th)
Try { throw th }
}
Use getOrElse(()) if you want to get value in case of success and default value (Unit) in case of failure

Mockito with Dependency Injection

I'm using Play Framework 2.5 and trying to test one of my DAO class based on the example here.
But my class under test has dependency injection and I wonder how to mock it.
I tried to do something but it seems a bit clumsy, and I would like help to improve it.
Here is my code :
class MyController #Inject()(val mydao: MyDAOClass) extends Controller {
def getData = Action { request =>
mydao.fetchData
}
}
class MyDAOClass #Inject()(ws: WSClient) {
def urls(): List[String] = { List("url1", "url2") }
// fetch data on different servers and should merge it.
def fetchData(): Future[List[String]] = {
val futures: List[Future[WSResponse]] = urls map { url =>
ws.url(url + "/some/data").withRequestTimeout(10000.millis).get()
}
futures map { future =>
future onComplete {
case Success(resp) => println(resp.json)
case Failure(t) => println("An error has occured: " + t.printStackTrace)
}
}
Future(List())
}
}
Here is my test code :
trait DAOTrait {
def urls: List[String]
}
class MyDAOClass extends PlaySpec with MockitoSugar {
"MyDAOClass" should {
"get a list of data" in {
Server.withRouter() {
case GET(p"/url1/some/data") => Action {
Results.Ok(Json.arr(Json.obj("name" -> "data1")))
}
} { implicit port =>
val mockMyDAOClass = mock[DAOTrait]
when(mockMyDAOClass.urls) thenReturn List("url1")
implicit val materializer = Play.current.materializer
WsTestClient.withClient { client =>
val mydao = new MyDAOClass(client) {
override def urls = mockMyDAOClass.urls
}
val result = Await.result(
mydao.fetchData(), 10.seconds)
result mustEqual List("data1")
}
}
}
}
}
The code is not finished in MyDAOClass, but it is enough to start implementing tests.
1) How to mock class that have dependency injection ?
2) When testing this code I have the following exception in MyDAOClass case Failure(t)
java.net.ConnectException: http://localhost:40183
at org.asynchttpclient.netty.channel.NettyConnectListener.onFailure(NettyConnectListener.java:137)
at org.asynchttpclient.netty.request.NettyChannelConnector$1.onFailure(NettyChannelConnector.java:106)
at org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:28)
at org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:20)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:683)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:564)
at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:425)
at io.netty.channel.nio.AbstractNioChannel.doClose(AbstractNioChannel.java:462)
at io.netty.channel.socket.nio.NioSocketChannel.doClose(NioSocketChannel.java:236)
at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:611)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:590)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:534)
at io.netty.channel.nio.NioEventLoop.closeAll(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:361)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.nio.channels.ClosedChannelException
but it should return Result.Ok instead.
3) Any advice welcome.