1
votes

I am creating a user registration module. On submission (using JSON), I want to check if the JSON parsed correctly. If there is a problem in JSON, I want to return error. If the JSON is correct, I want to check if the user already exists (look at firstname). Data is in MongoDB. I am using ReactiveMongoPlugin 0.10. I will use 'one' method which returns Future[Option[BSONDocument]]. How do I wait for this Future to finish before the Action completes?

Approach 1 - use Action and try to handle result of Future myself. Code doesn't compile and dont know how to wait for the Future to finish

  def registrationRequest = Action(parse.json) { request => {
    Logger.debug("received message:" + request)
    Logger.debug("received message:" + request.body)
    val jr:JsResult[User2] = request.body.validate[User2]
    Logger.debug( "jr is "+jr)

    jr match {
      case s:JsSuccess[User2] => {

        val user = s.get
        Logger.debug("opening database connection")

        val driver = new MongoDriver()
        val connection = driver.connection(List("localhost"))
        val db = connection.db("website-db")
        val collection = db.collection[BSONCollection]("users")

        // the data from client is a JSON of type {user:{firstname:"name"}}. I have created code to parse the JSON
        val query = BSONDocument("user"-> BSONDocument("firstname"->user.firstname))
        Logger.debug("query is:"+query)


        val result = collection.find(query).one

I want to now wait for result and return either an Ok(Json.toJson(ack)) or BadRequest(Json.toJson(ack)). How do i do that? I have written following code but I am stuck at two points (a) will the code wait for future to finish (b) onComplete returns Unit but Play's Action requires play.api.mvc.Result. How do I do that?

//I guess data would be Success or Failure
result onComplete ( data =>
        data match {
          //If Success, value would be Some or None
          case Success(value) => {
            value match {
              case None => { //no record. Can add
                Logger.debug("No record from mongo: Can add")
                val ack = Acknowledgment (1, "Welcome " + user.firstName + " " + user.lastName)
                Logger.debug ("success ack Json:" + Json.toJson (ack) )
                Ok (Json.toJson (ack) )

              }
              case Some(x) => { //duplicae record
                Logger.debug("error from Mongo. Duplicate:"+x)
                val ack = Acknowledgment(0,"duplicate: "+x.toString())
                Logger.debug("fail ack:"+Json.toJson(ack))
                BadRequest(Json.toJson(ack))
              }

            }
          }
          case Failure (e)=> {
            Logger.debug("error from Mongo."+e)
            val ack = Acknowledgment(0,"MongoDB Error: "+e.toString())
            Logger.debug("fail ack:"+Json.toJson(ack))
            BadRequest(Json.toJson(ack))
          }
        }) //onComplete returns Unit. Action needs play.api.mvc.Result
  case f:JsError => {
    Logger.debug("error: "+JsError.toFlatJson(f))
    val ack = Acknowledgment(0,JsError.toFlatJson(f).toString())
    Logger.debug("fail ack:"+Json.toJson(ack))
    BadRequest(Json.toJson(ack))
  }
}

}

Approach 2 - I read that I should use Action.async but I am unable to fit the pieces together. The 2nd approach I followed was to use Action.Async but the code didnt compile because it expects Future[SimpleResult]

def registrationRequest = Action.async(parse.json) { request => {
    Logger.debug("received message:" + request)
    Logger.debug("received message:" + request.body)
    val jr:JsResult[User2] = request.body.validate[User2]
    Logger.debug( "jr is "+jr)

    jr match {
      case s:JsSuccess[User2] => {

        val user = s.get
        Logger.debug("opening database connection")
        val driver = new MongoDriver()
        val connection = driver.connection(List("localhost"))
        val db = connection.db("website-db")
        val collection = db.collection[BSONCollection]("users")

        val query = BSONDocument("user"-> BSONDocument("firstname"->user.firstName))
        Logger.debug("query is:"+query)

        //result is of type Future[Option[BSONDocument]]
        val result = collection.find(query).one

        result.map(option => option match {
          case None => {
            //no record. Can add
            Logger.debug("No record from mongo: Can add")
            val ack = Acknowledgment(1, "Welcome " + user.firstName + " " + user.lastName)
            Logger.debug("success ack Json:" + Json.toJson(ack))
            Ok(Json.toJson(ack))
          }
          case Some(x) => {
            //duplicae record
            Logger.debug("error from Mongo. Duplicate:" + x)
            val ack = Acknowledgment(0, "duplicate: " + x.toString())
            Logger.debug("fail ack:" + Json.toJson(ack))
            BadRequest(Json.toJson(ack))
          }
        }
        )
      }
      case f:JsError => {
        Logger.debug("error: "+JsError.toFlatJson(f))
        val ack = Acknowledgment(0,JsError.toFlatJson(f).toString())
        Logger.debug("fail ack:"+Json.toJson(ack))
        BadRequest(Json.toJson(ack)) //Action.async expect scala.concurrent.Future[play.api.mvc.SimpleResult]
  }
}


  }
1

1 Answers

1
votes

The solution is to use Action.async which returns Future[SimpleResult]. Inside the code use map and flatMap on Future to return Future[SimpleResult].