I am using a Future.successful
with the Action.async
in playframework 2.6 with scala:
Code
package controllers
import com.google.inject.Inject
import play.api.Configuration
import play.api.libs.ws.{WSClient}
import play.api.libs.oauth.{ConsumerKey, RequestToken, OAuthCalculator}
import play.api.mvc.{AbstractController, ControllerComponents}
import play.api.libs.concurrent.CustomExecutionContext
import scala.concurrent.{Future, ExecutionContext}
import akka.actor.ActorSystem
trait MyExecutionContext extends ExecutionContext
class MyExecutionContextImpl @Inject()(system: ActorSystem)
extends CustomExecutionContext(system, "my.executor") with MyExecutionContext
class Application @Inject() (config: Configuration,
ws: WSClient,
myExecutionContext: MyExecutionContext,
cc: ControllerComponents ) extends AbstractController(cc) {
def credentials : Option[(ConsumerKey, RequestToken)] = for {
apiKey <- config.getOptional[String]("twitter.apiKey")
apiSecret <- config.getOptional[String]("twitter.apiSecret")
token <- config.getOptional[String]("twitter.token")
tokenSecret <- config.getOptional[String]("twitter.tokenSecret")
} yield (
ConsumerKey(apiKey.toString, apiSecret.toString),
RequestToken(token.toString, tokenSecret.toString)
)
def tweets = Action.async {
implicit val executor = scala.concurrent.ExecutionContext.global
credentials.map {
case (consumerKey, requestToken) => {
Future.successful {
ws
.url("https://stream.twitter.com/1.1/statuses/filter.json")
.sign(OAuthCalculator(consumerKey, requestToken))
.withQueryString("track" -> "reactive")
.get()
.map(response => {
Ok(response.body)
})
}
}
} getOrElse {
Future.successful {
InternalServerError("Twitter credentials are missing")
}
}
}
}
I have injected an execution context into the future as suggested here:
implicit val executor = scala.concurrent.ExecutionContext.global
To make the controller async, I used the blog post here
Error
Compiling 1 Scala source to /Users/localuser/Do/play-twitter-example/target/scala-2.12/classes ...
[error] /Users/localuser/Do/play-twitter-example/app/controllers/Application.scala:37:23: overloaded method value async with alternatives:
[error] [A](bodyParser: play.api.mvc.BodyParser[A])(block: play.api.mvc.Request[A] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[A] <and>
[error] (block: play.api.mvc.Request[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error] (block: => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent]
[error] cannot be applied to (scala.concurrent.Future[Object])
The method ws....get()
already returns a Future
so you don't need to wrap it inside a Future.successful
. You can simply write
def tweets = Action.async {
implicit val executor = scala.concurrent.ExecutionContext.global
credentials.map {
case (consumerKey, requestToken) => {
ws
.url("https://stream.twitter.com/1.1/statuses/filter.json")
.sign(OAuthCalculator(consumerKey, requestToken))
.withQueryString("track" -> "reactive")
.get()
.map(response => {
Ok(response.body)
})
}
} getOrElse {
Future.successful {
InternalServerError("Twitter credentials are missing")
}
}
}