0
votes

A bit new to Scala (latest), Reactivemongo (latest) and Play framework (2.3.x).

Validating sessions, and on success, loading the user object for it returning it as json. Problem is specific to mapping and futures I guess. The following code won't compile:

def userCheck(token: String) = Action.async {
  val futureSession = sessionCollection.find(Json.obj("token" -> token)).one[BSONDocument]

  futureSession.map {
    case Some(session) => {
      userCollection.find(Json.obj("_id" -> session.getAs[String]("userId").get )).one[JsObject].map {
        case Some(user) => Ok(JsObject(Seq("user" -> user)))
        case None => NotFound( JsObject(Seq("message" -> JsString("not found"))) )
      }
    }
    // NotFound( JsObject(Seq("message" -> JsString("not found"))) )
    case None => NotFound( JsObject(Seq("message" -> JsString("not found"))) )
  }
}

So, the error message is:

[error] AuthController.scala:186: overloaded method value async with alternatives:
[error]   [A](bodyParser: play.api.mvc.BodyParser[A])(block: play.api.mvc.Request[A] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[A] <and>
[error]   (block: play.api.mvc.Request[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error]   (block: => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent]
[error]  cannot be applied to (scala.concurrent.Future[Object])
[error]   def userCheck(token: String) = Action.async {

If you look at the source code you see a commented line, if I uncomment it, the code compiles. Am I missing to cover some cases?

UPDATE 1:

So, using the tips from the accepted answer, I came up with this:

def userCheck(token: String) = Action.async {
  getSession(token).flatMap {
    session => {
      session match {
        case Some(s) => {
          getUser( s.getAs[String]("userId").get ).flatMap {
            user => {
              user match {
                case Some(u) => Future.successful( Ok( u ) )
                case None => Future.successful( NotFound( JsObject(Seq( "m" -> JsString("User not found")))))
              }
            }
          }
        }
        case None => Future.successful( NotFound( JsObject(Seq( "m" -> JsString("Session not found")))))
      }
    }
  }
}

def getSession(token: String): Future[Option[BSONDocument]] = {
  sessionCollection.find( Json.obj("token" -> token) ).one[BSONDocument]
}

def getUser(id: String): Future[Option[JsObject]] = {
  userCollection.find( BSONDocument( "_id" -> BSONObjectID(id) ) ).one[JsObject]
}

...and it does the trick. Feels like refactoring is needed though.

1

1 Answers

2
votes

Try adding explicit types to all the branches; in particular, make sure the block after case Some(Session) returns a Result or whatever you think it should be. I suspect in the Some branch you're ending up with a Future[Future[Result]] rather than a Future[Result] - maybe you need to do futureSession.flatMap, and in the None case return Future.successful(...).