Search code examples
scalasecuresocial

Error on a method that return a Future[Option[BasicProfile]]


I'm writing a Play 2.3 application using SecureSocial(master version). I've created a LoginUser that represents a User Data in my system.

/**
 * Class representing a User in the system.
 * @constructor Create a new LoginUser instance from a BasicProfile object.
 * @param profile represents the BasicProfile object associated with the user.
 */
case class LoginUser(val profile: BasicProfile)

Now I'm trying to implement the UserService[T] trait (of SecureSocial)but i'm having trouble.

/**
 * Class tha handle all the request for read/write user data in/from the database.
 */
class InMemoryUserService extends UserService[LoginUser] {

  private var tokens = Map[String, MailToken]()
  /**
   * Finds a user by provider id and user id.
   * @param providerId - the user provider id.
   * @param userId - the user user id.
   * @return an optional user
   */
  def find(providerId: String, userId: String): Future[Option[BasicProfile]] = {
    val future: Future[Option[LoginUser]] = UserServiceLogin.find(Json.obj("providerId" -> providerId, "userId" -> userId)).one
    future onComplete {
      case Success(Some(x)) => return x.profile
      case _ => None
    }
}

The find method return a Future[Option[BasicProfile]] object, but the compiler tell me that the code is incorrect. Here the output of the compiler:

[error] /Users/alberto/git/recommendation-system/app/security/UserService.scala:68: type mismatch;
[error]  found   : securesocial.core.BasicProfile
[error]  required: scala.concurrent.Future[Option[securesocial.core.BasicProfile]]
[error]       case Success(Some(x)) => return x.profile

What's wrong?? How can i solve my problem??


Solution

  • You should not register a callback on your Future via onComplete. Instead, you want to map the content to change the type:

    def find(providerId: String, userId: String): Future[Option[BasicProfile]] = {
        val future: Future[Option[LoginUser]] = UserServiceLogin.find(Json.obj("providerId" -> providerId, "userId" -> userId)).one
    
        future map {
          case Some(x) => Some(x.profile)
          case None => None
        }
    }
    

    This should make the compile happy :) Note that the return type of onComplete is Unit, while by using map I can apply any change to the content of the Future.

    A less verbose version that maps both the Future and the Option:

    def find(providerId: String, userId: String): Future[Option[BasicProfile]] = {
        val future: Future[Option[LoginUser]] = UserServiceLogin.find(Json.obj("providerId" -> providerId, "userId" -> userId)).one
    
        future.map(_.map(_.profile)
    }