danbroooks
danbroooks

Reputation: 2800

Scala - filter Some's that fail certain condition

Given a UserRepository:

class UserRepository {

  val collection = Mongo.connect("users")

  def findBy(key: String, value: String): Future[Option[User]] = {
    collection.flatMap(
      _.find(document(key -> value)).one[User]
    )
  }
}

And I want to write a login method:

def login(email: String, password: String): Future[Option[User]] = {
  import scala.concurrent.ExecutionContext.Implicits.global

  repo.findBy("email", email)
    // .filter { user => BCrypt.checkpw(password, user.password) }
}

How would I write a mapping on the result of findBy to transform all Some(user)'s into None's if they fail the test BCrypt.checkpw(password, user.password) ? Is there some kind of transform I can do to turn any of the Some's that fail that check into None's?

Upvotes: 0

Views: 309

Answers (3)

insan-e
insan-e

Reputation: 3921

You probably don't want to do filter/collect on the Future because you'll get a failed Future (with exception), and then you need to .recover etc. Because result of your operation is already stated in it's signature (Option[User]) you probably want this:

repo.findBy("email", email).map { userOpt =>
  val maybeUser1 = userOpt.filter { user => BCrypt.checkpw(password, user.password) }

  // or like this:
  // longer form, if more than one options or whatever
  val maybeUser2 = for {
    user <- userOpt
    if(BCrypt.checkpw(password, user.password))
  } yield user
}

Upvotes: 3

Nagarjuna Pamu
Nagarjuna Pamu

Reputation: 14825

Use collect and use the pattern matching guard to check the user. If user fails then return a None

findBy(key, value).collect {
  case Some(user) if ! BCrypt.checkpw(password, user.password) =>  None
}

Suggestion

It would be good to collect user which satisfy the condition this way your final output type would be Future[user] instead of Future[Option[User]]. Of course after the above collect your Future[Option[User]] would contain Some(user) where is the user is valid user.

findBy(key, value).collect {
  case Some(user) if BCrypt.checkpw(password, user.password) =>  user
}

The line gives only valid users and the final type would be Future[User] instead of Future[Option[User]]

Upvotes: 2

marstran
marstran

Reputation: 28056

You need to map over the Future to transform its value from a Some to a None. The call to filter needs to go inside the function passed to the map-function. Try this:

def login(email: String, password: String): Future[Option[User]] = {
    import scala.concurrent.ExecutionContext.Implicits.global

    repo.findBy("email", email)
        .map { userOpt => userOpt.filter(user => BCrypt.checkpw(password, user.password)) }
}

Upvotes: 3

Related Questions