Play Scala Dependency injection: How to use it - scala

I am trying to use Play 2.5 dependency injection. I have following class which makes a call to REST api and parses the response
class Client #Inject()(ws:WSClient, baseUrl: string) {
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {....}
....
}
The caller of the code looks like below
var client = new Client(WS.client)
client.getResponse()
I am getting following warning.
object WS in package ws is deprecated: Inject WSClient into your
component
I understand that i need to inject WS.Client instead of passing it explicitly to the Client constructor. But how do i do that?
=== Update ===
I don't want to inject Client or WSClient from the Controller. My controller creates objects and classes at run time and i want those objects to create Client Object. When i explicitly pass WS.client object to the Client object i get the above stated warning.
=== Update 2 ===
I have a plugin architecture in my application. When a a controller starts an action. It does not know what set of plugins it is going to execute. Some plugins would not need a WSClient and some of them would. So i dont want to couple the injection of WSClient into my controller. Each plugin independently decides if it wants to call a remote service. When a plugin decides to call the remote service, it should be able to inject WSClient in what ever client it wants to invoke.
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1 (needs to call a remote api, create a client object, per say new Client(WS.Client)). This is where the injection should happen, not at the controller.

Ok. I will assume you have two classes. First we will have your Client class:
#Singleton // this is not necessary, I put it here so you know this is possible
class Client #Inject() (ws:WSClient, baseUrl: String) {
// Since this controller is not annotated with #Inject
// it WILL NOT be used when binding components
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {
// do something using ws object
}
}
Then you have another class that uses Client, per instance, a controller:
class MyController #Inject() (client: Client) extends Controller {
def someAction = Action {
// do something with client object
}
}
The main point here is that the controller did not need to create a Client instance. It was automatically injected by Guice.
Moreover, your client class needs a baseUrl and there is no place telling Play which value is needed there. If this is a configuration, than you can do something like this:
import play.api.Configuration
class Client #Inject() (ws:WSClient, configuration: Configuration) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
But, if you really want your Client object to receives a String, then we need to tell Play which String needs to be injected:
package com.acme.modules
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class MyModule extends AbstractModule {
def configure() = {
bind(classOf[String])
.annotatedWith(Names.named("baseUrl")) // attention to the name here. It will be used below
.toInstance("http://api.example.com/")
}
}
And then enable this module by adding the following line to your application.conf:
play.modules.enabled += "com.acme.modules.MyModule"
After that, we will change Client to be specific about which String it is expecting:
import play.api.Configuration
// #Named needs to receive the same value defined at the module class.
class Client #Inject() (ws:WSClient, #Named("baseUrl") baseUrl: String) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
Update after question edit:
Give the structure you want/need:
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1
Your code can also follow that path with classes like this:
MyController -> PluginResolver -> Plugin
-> PluginRunner ->
And, then, you can have:
Controller:
class MyController #Inject() (
pluginResolver: PluginResolver,
pluginRunner: PluginRunner
) extends Controller {
def action = Action {
val plugins = pluginsResolver.resolve(/* give a criteria to select plugins */)
val someResultFromPluginsExecution = pluginsRunner.run(plugins)
// map result from plugins execution to a play play.api.mvc.Result
// return the play.api.mvc.Result
}
}
Plugin classes:
import play.api.inject.Injector
class PluginResolver #Inject()(injector: Injector) {
def resolve(/* some criteria to resolve plugins */): Seq[Plugin] = {
val pluginsClasses = ... // find the necessary plugins based on the criteria
pluginsClasses.map { pluginClass => injector.instanceOf(pluginClass) }
}
}
// ExecutionContext is not really necessary, but maybe you want/need
// another thread pool to execute plugins
class PluginRunner #Inject()(implicit executionContext: ExecutionContext) {
def run(plugins: Seq[Plugin]): Seq[PluginExecutionResult] = {
// run the plugins
// return the result
}
}
trait Plugin {
def execute(): PluginExecutionResult
}
The real magic here happens at the PluginResolver. It uses a play.api.inject.Injector to create plugins instances and then your plugins can use Dependency Injection. Per instance:
class PluginThatNeedsWSClient #Inject(wsClient: WSClient) extends Plugin {
def execute(): PluginExecutionResult = {
// Use wsClient to call a remote service
// return the execution result
}
}
Reference:
Scala: Dependency Injection
Scala: Play WS API
play.api.inject.Injector

I saw this awesome post last week: http://www.schibsted.pl/2016/04/dependency-injection-play-framework-scala/

Related

How to Test a Play Application that extends a custom trait

I'm having trouble writing tests for a mixin to my Play application that runs in it's own thread separate from play. I've tried over-writing WithApplication.provideApplication method with no luck. I get an inheriting conflicting methods error. (one from the real app "MyRunnableSystemWrapper", one from my mocked fake mixin called "MyMockedSystemWrapper").
execute(system) runs my system that is tested elsewhere and has sideaffects (connects to networked services, thus failing this test when such things are not available. Good news is I have a mocked service of my system wrapper that uses a system which does NOT have side affects and DB/Network calls are mocked out. However I do not know how to give THIS MOCKED version of my app to "WithApplication" test.
Reduced Code for clarity:
class Application extends Controller with MyRunnableSystemWrapper {
val pool: ExecutorService = Executors.newFixedThreadPool(1)
val system = new MyRunnableSystem() //system is abstract in MRSW ^^^ above
pool.execute(system)
def index = Action {
OK("HI")
}
}
My Test:
class MyAppTest(implicit ee: ExecutionEnv) extends Specification {
abstract class WithMyMockApp extends WithApplication {
def provideApplication = new controllers.Application with MyMockedSystemWrapper // This imports MyRunnableSystemWrapper
}
"Sending a GET request" should {
"Respond with OK" in new WithMyMockApp {
val response = route(app, FakeRequest(GET, "/")).get
status(response) mustEqual OK
}
}
}
If I'm not running my Runnable in the correct place and should be calling it somewhere else to make this testing easier, let me know!
You could inject your system wrapper instead of extending it
trait SystemWrapper {
def execute(system: RunnableSystem)
}
class MyRunnableSystemWrapper extends SystemWrapper {...}
class MyMockedSystemWrapper extends SystemWrapper {...}
class Application #Inject() (systemWrapper SystemWrapper) extends Controller {
Then you need to tell Guice which implementation of SystemWrapper you want for runtime and which one for test. One way of doing this is by using different Guice modules for runtime/test which you set in your .conf files.

Manual Dependancy Injection App testing Play 2.5.x

I have project with manual dependency injection. Can I test my application with standard Play test suite?
play.application.loader="AppLoader"
class AppLoader extends ApplicationLoader {
override def load(context: Context): Application = {
LoggerConfigurator(context.environment.classLoader).foreach(_.configure(context.environment))
new AppComponents(context).application
}
}
}
class AppComponents(context: Context) extends BuiltInComponentsFromContext(context) with EhCacheComponents with EvolutionsComponents with DBComponents with HikariCPComponents{
lazy val applicationController = new controllers.Application(defaultCacheApi, dbApi.database("default"))
lazy val usersController = new controllers.Users(defaultCacheApi)
lazy val assets = new controllers.Assets(httpErrorHandler)
//applicationEvolutions
// Routes is a generated class
override def router: Router = new Routes(httpErrorHandler, applicationController, usersController, assets)
For now test is very simple
class ApplicationTest extends PlaySpec with OneAppPerTest {
"Application" must {
"send 404 on a bad request" in {
route(FakeRequest(GET, "/boum")) mustBe None
}
}
}
Test ends up with error:
Could not find a suitable constructor in controllers.Application. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument
I presume I need somehow use my AppLoader instead of defualt Guice mechanisam inside ApplicationTest class, because Application controller has dependacy ( cacheApi, dbApi ...)
route method can take application as argument but how can I obtain context to manually instantiate AppLoader class ? Being newbie in Scala recommendations are most welcomed.
This example answered all of my questions:
https://github.com/playframework/play-scala-compile-di-with-tests
Using term compile time dependency injection yield much more results then manual dependency injection.

How to perform unit test in controller?

Let say, i have controller like the following:
class JenisKejahatanControl #Inject()(service: JenisKejahatanService, val messagesApi: MessagesApi) extends Controller with I18nSupport {
def add = Action.async { implicit request =>
lazy val incoming = JenisKejahatan.formJenisK.bindFromRequest()
incoming.fold( error => {
lazy val response = ErrorResponse(BAD_REQUEST, messagesApi("request.error"))
Future.successful(BadRequest(Json.toJson(response)))
}, { newJenisK =>
lazy val future = service.addJenisK(newJenisK)
future.flatMap {
case Some(jenis) => Future.successful(Created(Json.toJson(SuccessResponse(jenis))))
case None => Future.successful(BadRequest(Json.toJson(ErrorResponse(NOT_FOUND, messagesApi("add.jenis.kejahatan.fail")))))
}
})
}
}
and the i want to test my def add using specs2, how to do it?
Since your controller has injected components the bit I assume that one bit you're missing is how to obtain an instance of it in your spec with the various dependencies satisfied. For this you can use the GuiceApplicationBuilder to obtain a Play application instance, and then use its injector to get an instance of your controller without having to construct it manually (more dependency injection docs here and specifically about testing with Guice here.)
If you can construct your controller manually, as in the example, that's great and makes things simpler, but controllers tend to have non-trivial dependencies which you will most likely want to mock using the overrides method on the GuiceApplicationBuilder.
With an instance of your controller constructed, it's then really simple to "apply" a mock (fake) request to your action methods and determine that they give the status and body you expect. Here's an example:
import controllers.{SuccessResponse, JenisKejahatanControl}
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.inject.bind
import play.api.mvc.Result
import play.api.test.{FakeRequest, PlaySpecification}
import scala.concurrent.Future
class JenisKejahatanControlSpec extends PlaySpecification {
"JenisKejahatanControl#add" should {
"be valid" in {
// Build an instance of a Play application using the
// default environment and configuration, and use it
// to obtain an instance of your controller with the
// various components injected.
val jenisController = new GuiceApplicationBuilder()
.overrides( // Mock the data service
bind[JenisKejahatanService]
.toInstance(new MockJenisKejahatanService))
.build()
.injector
.instanceOf[JenisKejahatanControl]
// Create a "fake" request instance with the appropriate body data
val request = FakeRequest().withFormUrlEncodedBody("name" -> "test")
// Apply the request to your action to obtain a response
val eventualResult: Future[Result] = jenisController.add.apply(request)
// Check the status of the response
status(eventualResult) must_== CREATED
// Ensure the content of the response is as-expected
contentAsJson(eventualResult).validate[SuccessResponse].asOpt must beSome.which { r =>
r.jenis.name must_== "test"
}
}
}
}

Decorating a Scala Play Controller with Java class Secured extends Security.Authenticator

I'm refactoring a Play 2.3 app in Java to Scala. Existing Java controllers are decorated like so for authentication.
#Security.Authenticated(Secured.class)
public class Application extends Controller { ... }
The signature of Secured.java is:
public class Secured extends Security.Authenticator { ... }
How might I decorate my Scala controller with the same Secured.java?
I've tried not doing that by writing a second Secured2.scala as a trait and doing authentication the Scala way in Play but many of the existing templates rely on Secured.java to get the current user so that's why I'm trying to make my Scala controller compatible with the Java Secured class.
I don't think you'd be able to use the same Java class for authentication using the scala API of play. But here's how you'd do it using the scala API and you can fill in the gaps with your Secured class code:
trait Authentication {
// Define what you want your auth header to be
val AUTH_TOKEN_HEADER = "X-AUTH-TOKEN"
object Authenticated extends Security.AuthenticatedBuilder(checkHeader(_), onUnauthorized(_))
def checkHeader(request: RequestHeader): Option[String] = {
request.headers.get(AUTH_TOKEN_HEADER) flatMap { token =>
// do a check to see if there is a user and get their name
}
}
def onUnauthorized(request: RequestHeader) = {
// Do something when the user isn't authorized to access a route
Results.Unauthorized
}
}
trait SecuredController extends Controller with Authentication
And here is how it would look in an actual controller:
object SomeController extends SecuredController {
def someApi = Authenticated { req =>
// do something
// The username is available in the request
val username: String = req.user
Ok
}
}

Play 2.0: FakeApplication calling a stub controller instead real one

This is a continuation of a prior question in which answer I saw that I had a misconception about the use of plugins in FakeApplication.
First I have a Model created as a trait and a object that implment it.
trait UserModel extends ModelCompanion[User, ObjectId] {
// ...
}
object User extends UserModel
Next, I have a Controller, created as a abstract class that receive a instance of UserModel, and its respective implementation that uses the User object.
abstract class UsersController extends Controller {
val userModel: UserModel
def sayHello = Action(parse.json) { request =>
// return a play Action. It doesn't use userModel
}
// Other methods
}
object Users extends UsersController(User)
In test directory, I created a UsersController Stub using a UserModel mock:
package controllers
import org.specs2.mock.Mockito
object UserControllersTest extends UsersController with Mockito {
val userModel = mock[models.UserModel]
}
Now I have my UsersControllers Spec test:
package controllers
import org.specs2.mutable.Specification
import play.api.libs.json.Json
import play.api.test._
import play.api.test.Helpers._
class UsersSayHelloSpec extends Specification {
running(FakeApplication()) {
"Users.SayHello" should {
def sendJson(jsonMap: Map[String, String], shouldBeCorrect: Boolean) = {
running(new FakeApplication(
additionalPlugins = Seq("controllers.UserControllersTest"))) {
// Preapration
val jsonRequisition = Json.toJson(jsonMap)
// ***************************************
// It will call UsersControllers.sayHello
val Some(result) = routeAndCall(FakeRequest(POST,
"/hello",
FakeHeaders(Map("Content-Type" -> Seq("application/json"))),
jsonRequisition))
// ***************************************
// ...
}
}
"Not process a empty String" in {
sendJson(Map.empty[String, String], false)
}
// Other tests calling sendJson ...
}
}
}
So my question is: How can I say to FakeApplication to use UserControllersTest instead the real UserControllers implementation when call "/hello" URL, in routeAndCall() calling?
There are two different part of your application which you might want to test:
The controller itself
The http router
When you test the controller itself, you typically create a request and you directly pass it to a controller method, which will generate the answer. You then perform some validation of the answer to verify the test result.
When you test the router, what you want to test is that the request is routed to the right controller method: you directly call the url and you check that the result is the one you expect from the controller you expected.
What you are trying to do does not make much sense to me:
if on route /hello I have a dummyController, will my post to the route
/hello works correctly?
Since the routes file is compiled into a Scala class which actually works as a router, if (and I am not sure) such a dynamic routing feature is available in play, you would have to:
Delete the existing route on the /hello path
Add a new route point to a different controller