user4063815
user4063815

Reputation:

Asynchronous wait for database-value in Playframework (2.4-M3) and Slick (3.0.0-RC3)

I'd like to keep my application as asynchronously as possible. Now I have this repository:

object LanguageRepository extends LanguageRepositoryTrait
{
    private val languages = TableQuery[Languages]
    private def db:Database = Database.forDataSource(DB.getDataSource())

    private def filterQuery(id: Long): Query[Languages, Language, Seq] = languages.filter(_.id === id)
    private def filterCode(code: String): Query[Languages, Language, Seq] = languages.filter(_.code === code)
    private def all() : Query[Languages, Language, Seq] = languages

    override def find(id: Long): Future[Language]  =
    {
        try db.run(filterQuery(id).result.head)
        finally db.close()
    }

    override def find(code: String): Future[Language] =
    {
        try db.run(filterCode(code).result.head)
        finally db.close()
    }

    override def get(): Future[Seq[Language]] =
    {
        try db.run(all().result)
        finally db.close()
    }
}

When I call a url like "domain.tld/{language}" I want to check whether the given language(code) actually exists. Like, if the site isn't available in french (fr) I want to throw an exception or a 404.

Now, my problem is that this whole asynchronously thing is pretty cool and while I do think to understand the theory behind it, I'm rather baffled right now. I mean, I want this to be non-blocking (and asynchronously, which is the reason for me using Future and async ;))

In my controller I want to do something like:

def checkLanguage(language:String) = Action
{
  val lang:Future[Language] = languageRepository.find(language)

 lang.onComplete
 {
   case Success(s) = Ok("Yay")
   case Failure(f) = 404("Oh no!")
 }
}

Of course this can't work, but that's a schema of how I want to have things working. I want to wait or postpone the rendering of the site, until it's confirmed that the given language-code is valid or invalid.

I had a look at the Playframeworks async-documentation for 2.3.6 (https://www.playframework.com/documentation/2.3.6/ScalaAsync) but I couldn't really get this to work as intended.

Any input appreciated!

Upvotes: 1

Views: 1149

Answers (2)

binkabir
binkabir

Reputation: 154

From your db query don't use .head instead use .headOption. that way ur return type will be a Future [Option [x]]

In ur controller u can do something like this

Lang.map { case Some (x) => Ok (x)
  case None =>  404 ( "not found ")

}

Upvotes: 1

mohit
mohit

Reputation: 4999

Try this,

Action.async {
    val lang:Future[Option[Language]] = languageRepository.find(language)
    lang.map {l => l.map{_ => Ok("Yay") }.getOrElse(NotFound("Oh no!"))
} 

First of all, I am assuming that if there is a possibility that a language will not exist then languageRepository.find(language) should return an Option of Language. Change Future[Language] to Future[Result] and use Action.async instead of Action

Now for some explanation, Action takes a block whose result should be Result. However, what you get is Future[Option[Language]]. Play provides async method for Action which needs Future[Result] and it takes cares of completing the request.

So, you need to convert Future[Option[Language]] to Future[Result].

 lang.map {l => l.map{_ => Ok("Yay") }.getOrElse(NotFound("Oh no!"))

We map over the lang, if the Option[Language] is not None then we convert it to Ok("yay") else we convert it to NotFound

Even, If you don't get Option[Language], the idea remains the same. Convert Future[Language] to Future[Result] and use Action.async instead of Action

Upvotes: 1

Related Questions