Search code examples
scalalagom

Scala for/yield with multiple optional calls based on credentials


I need to get an entire case class, built with 3 other case classes, back in one call. However, depending on a users credentials they may or may not have access to certain parts of that case class.

Below is how I'm currently doing it.(and it works) It's making the call but clearing out the unauthorized case classes afterwards. I would ideally like to stop the call (due to low bandwidth) if they don't have the proper credentials and return None.

The persistent entity service call is expecting an Option[PersonOne] and Option[PersonTwo] so the .ask must return that or it says it's returning an object and won't compile.

override def getPerson(id: UUID, credOne: Option[Boolean], credTwo: Option[Boolean]) = ServerServiceCall { _ =>
for {
  g <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral)
  h <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonOne) //if (credOne) **this 'if' does not work**

  s <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonTwo)
} yield {
  val x: Option[PersonOne] = if (credOne == Some(true)) h else None
  val y: Option[PersonTwo] = if (credTwo == Some(true)) s else None
  CompletePerson(g.get, x, y)

  //TODO catch if no employee is returned
  //throw NotFound (s"Person not Found with $id");
}

}


Solution

  • Here is a version of your function that will only call ask if the appropriate credential value is Some(true):

    override def getPerson(id: UUID, credOne: Option[Boolean], credTwo: Option[Boolean]) = ServerServiceCall { _ =>
      CompletePerson(
        registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral).get,
        credOne.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonOne)),
        credTwo.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonTwo))
      )
    }
    

    The key part is this:

    credX.filter(_ == true).flatMap(...)
    
    • If credX is None this will return None
    • If credX contains false it will return None
    • If credX is Some(true) then flatMap will call the ask function
    • If ask returns None then the expression will return None, otherwise it will return Some[PersonX]

    I am a bit unclear on some of the data types, but I think this should give you some idea of how to approach this code. (For example, that bare .get look dangerous as it could throw an exception)

    Edit after comments

    I fixed an issue with the filter(_), it should be filter(_ == true).

    It looks like ask actually returns Option[Option[T]], in which case this might be closer to what is needed.

    def getPerson(id: UUID, credOne: Option[Boolean], credTwo: Option[Boolean]) = ServerServiceCall { _ =>
      for {
        g <- registry.refFor[PersonEntity](id.toString()).ask(GetPersonGeneral)
        gen <- g
      } yield {
        val p1 = credOne.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonOne))).flatten
        val p2 = credTwo.filter(_ == true).flatMap(_ => registry.refFor[PersonEntity](id.toString()).ask(GetPersonTwo))).flatten
    
        CompletePerson(gen, p1, p2)
      }
    }