Search code examples
scalaconcurrent.futuresplayframework-2.6

scala.concurrent.Future[play.api.mvc.Result] required: play.api.mvc.Result


I want an Action.async that (1) try to get values from the DB. If the DB is not available, it will try to connect to another resource and (2) get the values from there. Because the two resources that I am using return Future, I am separating them with "recover" keyword. I am not sure if it is the best way..... But the statement inside the recovery{} has a type mismatch error:

  def show(url: String) = Action.async { implicit request: Request[AnyContent] =>
    println("url: " + url)

    val repositoryUrl = RepositoryUrl(url)
    val repositoryId = RepositoryId.createFromUrl(url)

    // Listing commits from the DB
    val f: Future[Seq[Commit]] = commit.listByRepository(repositoryId.toString())
    f.map { f: Seq[Commit] =>
      val json = JsObject(Seq(
        "project URL" -> JsString(url),
        "list of commits" -> Json.toJson(f)))
      Ok(json)
    }.recover {
      case e: scala.concurrent.TimeoutException =>
        // InternalServerError("timeout")
        // Listing commits from the Git CLI
        val github = rules.GitHub(repositoryUrl)
        val seq: Future[Seq[Commit]] = github.listCommits

        seq.map { seq: Seq[Commit] =>
          val json = JsObject(Seq(
            "project URL" -> JsString(url),
            "list of commits" -> Json.toJson(seq)))
          Ok(json)
        }
    }
  }

I am getting the error type mismatch; found : scala.concurrent.Future[play.api.mvc.Result] required: play.api.mvc.Result on the line seq.map { seq: Seq[Commit] =>. How can I return another result if I have a failure from my future?

Thanks!


Solution

  • recover wraps plain result in Future for you (analogue of map), while recoverWith expects Future as the result (analogue of flatMap). (https://stackoverflow.com/a/36585703/5794617). So, you should use recoverWith:

    def show(url: String): EssentialAction = Action.async { implicit request: Request[AnyContent] =>
      // This future will throw ArithmeticException because of division to zero
      val f: Future[Seq[Int]] = Future.successful(Seq[Int](1, 2, 3, 4 / 0))
      val fResult: Future[JsObject] = f.map { r =>
        JsObject(Seq(
          "project URL" -> JsString(url),
          "list of commits" -> Json.toJson(r)))
      }.recoverWith {
        case e: ArithmeticException =>
          val seq: Future[Seq[Int]] = Future.successful(Seq(1, 2, 3, 4))
    
          seq.map { seq: Seq[Int] =>
            JsObject(Seq(
              "project URL" -> JsString(url),
              "list of commits" -> Json.toJson(seq)))
          }
      }
      fResult.map { r =>
        Ok(r)
      }
    }