1

Give then following code:

AccountsDAO.scala:

def find(id: EntityId): Future[Option[Account]] =
    collection.find(Json.obj(Fields.Id -> id)).one[Account]

And AccountService.scala:

def getAccount(id: Option[Credentials]) = id.flatMap {
    accountId => accountDAO.find(accountId.accountId) //Throws an error
  }

The commented line above throws this error:

type mismatch; found : scala.concurrent.Future[Option[models.Account]] required: Option[?]

What am I missing? Why does flattop return an Option[?]. If I change the return type of the getAccount method as:

  def getAccount(id: Option[Credentials]): Future[Option[Account]] = id.flatMap {
    accountId => accountDAO.find(accountId.accountId) //Still throws an error
  }

I get the error below:

type mismatch; found : Option[Nothing] required: scala.concurrent.Future[Option[models.Account]]

What is going on? What am I missing?

Thanks in advance.

Edit : Here's the code in the controller and what I'm trying to do:

  def auth = Action.async(parse.json) { request =>
    {

      val authRequest = request.body.validate[AuthRequest]
      authRequest.fold(
        errors => Future(BadRequest),
        auth => {
          //First verify username and password
          val authRequestResult = for {
            validCredentials <- credentialsManager.checkEmailPassword(auth.email, auth.password)
            account:Option[Account] <- accountManager.getAccount(validCredentials)
            session:Session <- sessionManager.createSession(account.get.id, account.get.roles)

            touchedSession <- sessionManager.TouchSession(session.id)

          } yield AuthResponse(session.id, session.token, account.get.id, account.get.roles)

          authRequestResult map {
            case res: AuthResponse => Ok(Json.toJson(res))
            case _ => NotFound

          }
        })
    }
  }

The checkEmailPassword method above returns a Future[Option]:

def checkEmailPassword(email: String, password: String) =
    for {
      credentials <- credentialsDAO.find(AuthType.EmailPassword, email)
      validPassword <- BCrypt.checkFuture(password, credentials)

    } yield (credentials)

And credentialsDAO.find:

def find(authType: AuthType.Value, authAccountId: String) =
    collection.find(
      Json.obj(Fields.AuthType → authType,
        Fields.AuthAccountId → authAccountId)).one[Credentials].recover(wrapLastError)

So when checkEmailPassword returns an Option[Credentials] object, is it okay to assume the for comprehension in the controller will not execute any further if it returns a None? Then I could just say something like account:Option[Account] <- accountManager.getAccount(validCredentials.get.id). Is there a better way to structure/organize this code? Any patterns that I can follow/use?

MojoJojo
  • 3,897
  • 4
  • 28
  • 54
  • You are flatmapping an `Option` (the `id`) with a `Future` (the `find` result). Even if that is a `Future[Option[T]]`, that's not valid. – cchantep Jun 12 '16 at 07:52
  • If you plan to implement `getAccount` by calling `find`, which requires an `id: EntityId`, the `id` of `getAccount` has to be non-optional there. You can deal with "missing" ID before that (e.g. in controller). – cchantep Jun 12 '16 at 07:57
  • Thank you for the suggestion. I have posted more code for the controller and other DAO to clarify what I'm trying to do. Is there a better way to structure this code? Any patterns etc. that I can reuse? – MojoJojo Jun 12 '16 at 08:09
  • I guess `checkEmailPassword` returns a `Future[Option[T]]`. It better returns a `Future[T]` (missing a matching credential is an auth failure for me). In this way, the `id` passed to `getAccount` is not optional. – cchantep Jun 12 '16 at 08:19
  • Please, look into this question and answer: http://stackoverflow.com/questions/37771671/scala-play-future-interdependancy. You cannot `flatMap` `Option` and `Future` in scala together "out of the box". – liosedhel Jun 12 '16 at 09:49

0 Answers0