Compilation error for Future method [Option [BasicProfile]]

I am writing a 2.3 app for a game using a secure social library and an updated library with scala. Now I am trying to implement the UserService [T] property, but I am getting compile errors in the updatePasswordInfo method. This is the method:

def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
    implicit val passwordInfoFormat = Json.format[PasswordInfo]
    //the document query
    val query = Json.obj("providerId" -> user.providerId,
                         "userId" -> user.userId
                        )
    //search if the user exists
    val futureUser: Future[Option[LoginUser]] = UserServiceLogin.find(query).one
    futureUser map {
      case Some(x) => val newPassword = Json.obj("passswordInfo" -> info)// the new password
                      UserServiceLogin.update(query, newPassword) //update the document
                      val newDocument: Future[Option[LoginUser]] = UserServiceLogin.find(query).one
                      newDocument map {
                        case Some(x) => x
                        case None => None

                      } //return the new LoginUser
      case None => None
    }

  }

      

And this is a compiler error:

/Users/alberto/git/recommendation-system/app/security/UserService.scala:203: type mismatch;
[error]  found   : scala.concurrent.Future[Product with Serializable]
[error]  required: Option[securesocial.core.BasicProfile]
[error]                       newDocument map {

      

What's wrong?

+3


source to share


3 answers


If you really want the search to complete quickly (although it's not that useful) and then reload the updated user from the database, something like this should do without the need for scalaz:



def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
    implicit val passwordInfoFormat = Json.format[PasswordInfo]
    //the document query
    val query = Json.obj("providerId" -> user.providerId,
                         "userId" -> user.userId)
    val newPassword = Json.obj("passswordInfo" -> info)
    //update the document
    for {
        userO <- UserServiceLogin.find(query).one[BasicProfile] //fail fast (not sure this is really useful)
        updatedUser<-UserServiceLogin.update(query, newPassword).map(_=>userO).recover{case _ =>None}
        actualUser <- UserServiceLogin.find(query).one[BasicProfile]
    } yield actualUser

  }

      

+1


source


If you type Future[A]

, you get Future[B]

where T

is the return type from the lambda you are passing to map

.

Since this lambda returns Future[B]

, in this case you will get Future[Future[B]]

one that does not match the expected type.

An easy fix is ​​to use flatMap

that accepts a lambda going from A

to Future[B]

.


Also, you are returning Option[LoginUser]

, but the method is expected to return Option[BasicProfile]

. The compiler has inferred a common supertype, which is in this case Product with Serializable

, since they are both case classes.

Summarizing

scala.concurrent.Future[Product with Serializable]
^_____________________^^_________________________^
          1                        2

      

  • use flatMap

    insteadmap

  • return BasicProfile

    instead of LoginUser

    or change the return type of the method toFuture[Option[LoginUser]]




By the way, there is a lot of room for improvement, as you could use OptionT

scalaz's monad transformer to make things look nicer.

Here's an example

import scalaz._; import Scalaz._

val newPassword = Json.obj("passswordInfo" -> info)
(for {
  // this is done only for failing fast in case the user doesn't exist
  _ <- optionT(UserServiceLogin.find(query).one)
  _ <- optionT(Future.successful(Some(UserServiceLogin.update(query, newPassword))))
  updatedUser <- optionT(UserServiceLogin.find(query).one)
} yield updatedUser).run

      

By the way, this works on the assumption that it update

is a synchronous call, which may (and hopefully) not. If it returns Future[T]

, just change the code to

_ <- optionT(UserServiceLogin.update(query, newPassword).map(Some(_)))

      

or if it already returns Future[Option[T]]

_ <- optionT(UserServiceLogin.update(query, newPassword))

      

+2


source


There are several ways to improve your code.

For example, you don't need to search for a user before running a query.

Also, it would be nice to check if your request was indeed successful (if the API allows it).

Third, I'm not sure how LoginUser corresponds to BasicProfile. Your code doesn't seem to be doing any conversion. If LoginUser is a subclass of BasicProfile, or can be passed to BasicProfile, you can try something like this:

def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
    implicit val passwordInfoFormat = Json.format[PasswordInfo]
    //the document query
    val query = Json.obj("providerId" -> user.providerId,
                         "userId" -> user.userId
                        )
    UserServiceLogin.update(query, newPassword) //update the document
    for {
        user <- UserServiceLogin.find(query).one
    } yield user.map(_.asInstanceOf[BasicProfile]) //return the new LoginUser

  }

      

+1


source







All Articles