Невозможно использовать для понимания, чтобы решить будущее - PullRequest
0 голосов
/ 17 сентября 2018

У меня есть это Action, которое должно возвращать Future[Result], но я не могу кодировать его, используя for понимание. Это первый раз, когда я использую for понимание, поэтому я также не уверен, должен ли я использовать for.

Кроме того, кто-нибудь прокомментирует, правильно ли использовать for?

def verifyUser(token:String) = Action.async{
    implicit request => { //the function takes a token
      val tokenFutureOption:Future[Option[UserToken]] = userTokenRepo.find(UserTokenKey(UUID.fromString(token))) //checkc if the token exists in the db (db returns a Future)
      for(tokenOption<- tokenFutureOption) yield { //resolve the future
        tokenOption match {  
          case Some(userToken) =>{//token exists
            val userOptionFuture = userRepo.findUser(userToken.loginInfo)//find user to which the token belongs. Another db request which returns a Future
            for(userOption <- userOptionFuture) yield {//resolve future
              userOption match {
                case Some(user) =>{//user exists
                  val newInternalProfile = user.profile.internalProfileDetails.get.copy(confirmed=true) //modify user's profile
                  val newProfile = UserProfile(Some(newInternalProfile),user.profile.externalProfileDetails)
                  val confirmedUser = user.copy(profile=newProfile)
                  val userOptionFuture :Future[Option[User]] = userRepo.updateUser(confirmedUser) //update profile with new value. Another db operation with returns a Future
                  for(userOption <- userOptionFuture) yield {//resolve future
                  userTokenRepo.remove(UserTokenKey(UUID.fromString(token)))//remove the token
              //      Ok("user verified") //I WANT TO RETURN SUCCESS RESPONSE HERE BUT CODE DOESN'T COMPILE IF I UNCOMMENT THIS
                  }
                }
                case None =>{ //user doesn't exist
            //      Ok("user verified") //I WANT TO RETURN FAILURE RESPONSE HERE BUT CODE DOESN'T COMPILE IF I UNCOMMENT THIS
                }
              }
            }    
          }
          case None =>{//INVALID TOKEN RECEIVED
            Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config //I CAN RETURN Redirect (WHICH IS OF SAME TYPE AS OK I.E. RESULT) BUT WHY AM I NOT ABLE TO USE OK ABOVE
          }
        }

      }//THIS IS THE END OF FIRST FOR LOOP. HOW DO I HANDLE FAILURES IN THE FUTURE?

    }
  }

1 Ответ

0 голосов
/ 21 октября 2018

Вы используете Future, Option, которые являются монадами, и идея, лежащая в их основе, помогает упорядочить вычисления точно так же, как Functor s, но также позволяет указать, что будет дальше. .flatMap - это то, что Монады допускают, что может использоваться как for yield для удобства чтения.

Так что в вашем примере вы можете составить API, используя последовательность операций.

  object Api {

    final case class UserTokenKey(uuid: UUID)
    final case class UserToken(loginInfo: String)

    object userTokenRepo {
      def find(u: UserTokenKey) = {
        Future.successful(Some(UserToken(loginInfo = "foundLoginInfo")))
      }

      def remove(u: UserTokenKey) = {
        Future.successful(Some(UserToken(loginInfo = "")))
      }
    }

    final case class User(profile: String)

    object userRepo {
      def findUser(u: String) = {
        Future.successful(Some(User("profile")))
      }

      def updateUser(u: User) = {
        Future.successful(Some(User("updated profile")))
      }
    }

    def verifyUser(token: String)(implicit executionContext: ExecutionContext) = {

      val user: Future[Option[UserToken]] = for {
        tokenMaybe: Option[UserToken] <- userTokenRepo.find(UserTokenKey(UUID.fromString(token)))
        userMaybe: Option[User] <- 
          tokenMaybe match {
            case Some(t) => userRepo.findUser(t.loginInfo)
            case _ => Future.successful(Option.empty[User])
          }
        updatedUserMaybe: Option[User] <- 
          userMaybe match {
          case Some(u) => userRepo.updateUser(u)
          case _ => Future.successful(Option.empty[User])
        }
        removedUserMaybe: Option[UserToken] <- userTokenRepo.remove(UserTokenKey(UUID.fromString(token)))
      } yield removedUserMaybe

      user
    }

    def httpLayer(request: String) = {
      import scala.concurrent.ExecutionContext.Implicits.global
      import play.api.mvc.Results

      verifyUser(request).onComplete {
        case Success(Some(user)) =>
          println("user verified")
          Results.Ok("user verified")
        case Success(None) =>
          println("user does not exist")
          Results.Ok("user does not exist")
        case Failure(f) =>
          println("unknown error")
          Results.Ok("unknown error")
      }

    }

  }

Теперь вы можете проверить свой API, как показано ниже

  def main(args: Array[String]): Unit = {

    import Api._

    httpLayer(UUID.randomUUID().toString) // user verified

  }

Примечание 1 : возможно, вы захотите использовать api для ответа Future[Either[Error, User]], чтобы лучше обрабатывать ошибки, и использовать Error, чтобы определить, что реагировать на http-потребителей.

Примечание2 : Поскольку вы работаете с двумя монадами Future[Option[a]], вы можете объединить их, используя Преобразование монад OptionT[OuterMonad, Value Type] в scalaz или библиотеку CATS MTL. , Который дает одну монаду OptionT.

def verifyUserV2(tokenString: String)(implicit executionContext: ExecutionContext) = {

      import scalaz._
      import Scalaz._

      // import cats.implicits._
      // import cats.data.OptionT

      val userStack = for {
        token       <- OptionT(userTokenRepo.find(UserTokenKey(UUID.fromString(tokenString))))
        user        <- OptionT(userRepo.findUser(token.loginInfo))
        updatedUser <- OptionT(userRepo.updateUser(user))
        removedUser <- OptionT(userTokenRepo.remove(UserTokenKey(UUID.fromString(tokenString))))
      } yield removedUser

      userStack.run
      //cats unpack stack
      // userStack.value
}

Полезные чтения:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...