Zuriar
Zuriar

Reputation: 11734

Starting another Future call from within a Future.traverse

I am having trouble calling out to another function which returns a Future from within a Future.traverse. The scenario is that I have a Seq[Document] from which I need to turn them into a Future[Seq[NewsArticle]]. However, in order to do this I need to take the doc.categoryID and use it to call out to another API to build the NewsCategory, which will be returned as a Future[NewsCategory]. My problem is not knowing how to squish this in to the Future.traverse after it has returned.

Here is my scala code so far:

object NewsArticle {

  def buildNewsArticles(docs:Seq[Document]):Future[Seq[NewsArticle]] = {
     Future.traverse(docs) { doc => 
        val categoryID = doc.catID
        val title = doc.title
        val pdf = doc.pdfLink
        val image = doc.imageUrl
        val body = doc.body

        //need to call NewsCategory.buildNewsCategory(categoryID)
        //but it returns a Future[NewsCategory]
        //can't use for-yield because it still only yields
        //a Future

        future(NewsArticle( ??? ,title, pdf, image, body)
     }

  }

}

object NewsCategory {

  def buildNewsCategory(catID:String):Future[NewsCategory] = {
     // Calls external api across the wire and 
     // Returns a Future[NewsCategory]
  }

}

// CASE CLASSES

case class NewsArticle(
    category:NewsCategory,
    title:String,
    pdf:String,
    image:String,
    body:String)

case class NewsCategory(
    id:String,
    name:String
    description:String)

Thanks for helping

Upvotes: 1

Views: 118

Answers (2)

atretkow
atretkow

Reputation: 362

Maybe try this:

object NewsArticle {

  def buildNewsArticles(docs:Seq[Document]):Future[Seq[NewsArticle]] = {
     Future.traverse(docs) { doc => 
        val categoryID = doc.catID
        val title = doc.title
        val pdf = doc.pdfLink
        val image = doc.imageUrl
        val body = doc.body

        for {
          cat <- NewsCategory.buildNewsCategory(categoryID)
        } yield new NewsArticle(cat, title, pdf, image, body)

     }
  }
}

Upvotes: 1

Hugh
Hugh

Reputation: 8932

It sounds like you want to create a NewsArticle instance based on the NewsCategory returned in the future from buildNewsCategory, which means that you were on the right track. I think the following will do what you want:

Future.traverse(docs) { doc => 
    val categoryID = doc.catID
    val title = doc.title
    val pdf = doc.pdfLink
    val image = doc.imageUrl
    val body = doc.body

    // This could be a for-yield instead as you note.
    NewsCategory.buildNewsCategory(categoryID).map { category =>
      NewsArticle(category, title, pdf, image, body)
    }
 }

Upvotes: 3

Related Questions