How to in Scala handle NoSuchElementException without throwing exception - scala

I'm using Play framework by Scala. I have Postgresql database. It fetches data by following code:-
def eventById(id: Long): Option[EventRow] = {
val action = events.filter(_.id === id)
val results = db.run(action.result.head)
val notFound = None: Option[EventRow]
try {
Some(Await.result(results, Duration.Inf))
} catch {
case e: Exception => Logger.info(s"Failed to fetch event by id: $e.")
notFound
} finally {
}
}
}
Here in case data not bound it throws exception. Here I don't want to throw exception.I want to return notFound. I cannot even compile without throwing Exception.
Is there a way to return notFound if event not found in database?
Please let me know? Thanks!

Try:
def eventById(id: Long): Option[EventRow] = {
val action = events.filter(_.id === id)
val res: Future[Option[EventRow]] = db.run(action.result.headOption)
Await.result(res, Duration.Inf)
}

Related

Scala "Try" return type and exception handling

I am a newbie for Scala and now am trying to complete an exercise. How can I return an InvalidCartException while the function return type is Try[Price]
//Success: return the calculated price
//Failure: InvalidCartException
def calculateCartPrice(cart:Cart): Try[Price] = {
if(isCartValid(cart)) {
//Calculations happen here
return Try(Price(totalPrice));
}
}
def isCartValid(cart: Cart): Boolean = {
//THIS WORKS FINE
}
Thank you for the help
If you mean "how to make the Try contain an exception", then use the Failure() like below:
def calculateCartPrice(cart:Cart): Try[Price] = {
if(isCartValid(cart)) {
//Calculations happen here
Success(Price(totalPrice));
} else {
Failure(new InvalidCartException())
}
}
Then, given a Try you can use getOrElse to get the value of success or throw the exception.
Try will catch the exception for you, so put the code that can throw the exception in there. For example
def divideOneBy(x: Int): Try[Int] = Try { 1 / x}
divideOneBy(0) // Failure(java.lang.ArithmeticException: / by zero)
If what you have is a Try and you want to throw the exception when you have a Failure, then you can use pattern matching to do that:
val result = divideByOne(0)
result match {
case Failure(exception) => throw exception
case Success(_) => // What happens here?
}
The Neophyte's Guide to Scala has lots of useful information for people new to Scala (I found it invaluable when I was learning).

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

Catching scanamo errors within scala

I'm using scanamo to query a dynamodb table.
I want to create a healthcheck method that will return true as long as dynamo can be scanned (i don't care if there are records in the table, i just want to know that the table is there and can be scanned). And i want false to be returned if the table is not present (i fudge the table name as a test).
This is the scala code i have right now:
trait DynamoTestTrait extends AbstractDynamoConfig {
def test(): Future[List[Either[DynamoReadError, T]]] =
ScanamoAsync.exec(client)(table.consistently.limit(1).scan())
}
and it works when the table is present:
val r = reg.test()
.map(
_.headOption.forall(_.isRight)
)
val e = Await.result(r, scala.concurrent.duration.Duration(5, "seconds"))
but when the table name is incorrect i get an unexpected error:
Unexpected server error: 'Cannot do operations on a non-existent table (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ResourceNotFoundException;
I would have thought that i was trapping that error in my Left.
How can i trap this error? does scala have a try/catch construct i could use?
I also tried this but the error still gets thrown and not my catch it seems:
trait DynamoTestTrait extends AbstractDynamoConfig {
def test(): Future[Boolean] =
try {
ScanamoAsync
.exec(client)(table.consistently.limit(1).scan())
.map(
_.headOption.forall(_.isRight)
)
} catch {
case e: ResourceNotFoundException => Future.successful(false)
}
}
this seems more scalathonic, but always returns true:
trait DynamoTestTrait extends AbstractDynamoConfig {
def test: Future[Boolean] = {
val result = Try(ScanamoAsync
.exec(client)(table.consistently.limit(1).scan())
.map(
_.headOption.forall(_.isRight)
))
result match{
case Success(v) => Future.successful(true)
case Failure(e) => Future.successful(false)
}
}
}

How to add logging to try/catch with the borrowing pattern

How can I add logging in the case of an exception in the code below?
I tried to add a catch, but then I get an error saying that Unit does not conform to the type T.
Is there a trick around this somehow?
private def withClient[T](body: Jedis => T): T = {
var jedis: Jedis = null
try {
jedis = pool.getResource
body(jedis)
}
catch {
case _ => println("hllo")
}
finally {
if(jedis != null) jedis.close()
}
}
The problem is that in the case of an exception you are returning the value of println("hllo") which is Unit
You need to change the return type to something that represents the fact that you may not have a value, I'd suggest Try[T] or if you don't care to carry the exception data out of the function you could use Option[T]
private def withClient[T](body: Jedis => T): Try[T] = {
var jedis: Jedis = null
Try(
try {
jedis = pool.getResource
body(jedis)
}
catch {
case e =>
println("hllo")
throw e
}
finally {
if(jedis != null) jedis.close()
}
)
}
or
private def withClient[T](body: Jedis => T): Option[T] = {
var jedis: Jedis = null
try {
jedis = pool.getResource
Some(body(jedis))
}
catch {
case e =>
println("hllo")
None
}
finally {
if(jedis != null) jedis.close()
}
}
If your body function throws an exception instead of returning a T,
there's pretty much nothing you can do--you have to propagate the
exception forward instead of returning a T from withClient. Try is
the right approach to handle this because it safely captures the
exception and allows you to do something only if there was an exception
thrown. E.g.,
private def withClient[T](body: Jedis => T): T = {
val jedis = pool.getResource
val tryT = scala.util.Try(body(jedis))
jedis.close()
tryT.failed foreach { e => println(e.getMessage) }
tryT.get
}
The following points are notable:
We got rid of null and var
We don't need to worry about pool.getResource throwing an exception
because if it does, we can't go ahead anyway and we don't have to
worry about closing the resource because the exception means it was
never opened in the first place.
We make sure we close the jedis resource regardless of exception
because if there was an exception, it would've been safely caught in
the Try
We print a message only if there is any exception
We 'force' a value using Try#get only if there was no exception, and
if there was an exception, it will get rethrown here.

How to use StaticQuery in Slick 3.0.0?

In Slick 2.1 I had the code below to execute an sql-query from a file:
def fetchResult[T](sql: String)(implicit getResult: GetResult[T]): List[T] = {
val query = Q.queryNA[T](sql)
try {
Database.forDataSource(DB.getDataSource())
.withSession { implicit session => query.list }
}
catch {
case e: Throwable =>
throw new RunSqlException(s"Query $name execution error", e)
}
}
In Slick 3.0.0 you use dbConfig.db.run method to execute DBIOAction and get a future of the result. But I can't find a way to transform result of Q.queryNA (which is StaticQuery[Unit, R]) into DBIOAction. Does such a way exist?
I ended up with deprecated calls for now. Help me be better!
def fetchResult[T](sql: String)(implicit getResult: GetResult[T]): Future[List[T]] = Future {
val query = Q.queryNA[T](sql)
try {
this.dbConfig.db.withSession { implicit session => query.list }
}
catch {
case e: Throwable =>
throw new RunSqlException(s"Query $name execution error", e)
}
}
Only solution I managed to find was a bit hackish:
import slick.driver.HsqldbDriver.api._
def fetchResult[T](sql: String) = {
database.run(sqlu"""#$sql""")
}