VBAHole
VBAHole

Reputation: 1518

Evaluating a Scala List[Either] into a Boolean

I am using scanamo to query a dynamodb and all i want to do is check that the db actually exists. I'm not really concerned with the record I get back, just that there were no errors. For the query part I'm using this:

trait DynamoTestTrait extends AbstractDynamoConfig {  def test(): Future[List[Either[DynamoReadError, T]]] =    ScanamoAsync.exec(client)table.consistently.limit(1).scan())}

that returns the Future List. I want to evaluate the first? item in the list and just return true if it is not a read error.

I thought this would work but it doesn't:

 val result = test() match {
      case r: DynamoReadError => Future.successful(false)
      case r: Registration    => Future.successful(true)
    }

I'm new to scala so struggling with return types and things. This is a Play api call so i need to evaluate that boolen future at some point. With something like this:

 def health = Action {
    val isHealthy = h.testDynamo()
    val b: Boolean = Await.result(isHealthy, scala.concurrent.duration.Duration(5, "seconds"))
    Ok(Json.toJson(TestResponse(b.toString)))
  }

I think this is probably wrong also as i don't want to use Await but i can't get async to work either. Sorry, i'm kind of lost.

When i try to evaluate result i only get a message about the Future:

{
"status": 500,
"message": "Future(<not completed>) (of class scala.concurrent.impl.Promise$DefaultPromise)"
}

Upvotes: 0

Views: 395

Answers (1)

Tim
Tim

Reputation: 27356

The result is a Future so you can't test the result without doing something like Await.result (as you do later). What you can do is modify the result returned by the Future to be the result you need.

In your case you can do this:

test().map(_.headOption.forall(_.isRight))

This will return Future[Boolean] which you can then use in your Await.result call.

Here is how it works:

  • map calls a function on the result of the Future, which is type List[Either[DynamoReadError, T]] and returns a new Future that gives the result of that function call.

  • _.headOption takes the head of the list and returns an Option[Either[DynamoReadError, T]]. This is Some(...) if there are one or more elements in the list, or None if the list is empty.

  • forall checks the contents of the Option and returns the result of the test on that option. If the Option is None then it returns true.

  • _.isRight tests the value Either[...] and returns true if the value is Right[...] and false if it is Left[...].

This does what you specified, but perhaps it would be better to check if any of the results failed, rather than just the first one? If so, it is actually a bit simpler:

test().map(_.forall(_.isRight))

This checks that all the entries in the List are Right, and fails as soon as a Left is found.

The problem with returning this from Play is a separate issue and should probably be in a separate question.

Upvotes: 2

Related Questions