Any idea why this code doesn't work? I'm trying to have serialize method to all my exceptions to avoid the need for pattern matching for every exception. With the code below, I just need to case ex:MarshallableException => ex.serialize
The below code can run stand-alone. It will print Start... Serialization Failure shared.utils.InvalidID :(
import scala.util.{Failure, Success}
import akka.http.scaladsl.marshalling.{Marshaller, Marshal}
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.ResponseEntity
import spray.json.DefaultJsonProtocol
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
trait MarshallableException{
def serialize: Future[ResponseEntity]
}
case class InvalidID(error:String, code: String= "6001") extends Exception with MarshallableException {
import Exceptions._
override def serialize: Future[ResponseEntity] = {
Marshal(this).to[ResponseEntity]
}
}
object Exceptions extends DefaultJsonProtocol{
implicit val invalidIDFormat = jsonFormat2(InvalidID.apply)
}
object Test extends App{
val ex = new InvalidID("Test exception")
println("Start...\n")
ex.serialize.onComplete{
case Success(e) => println(e.toString())
case Failure(f) =>
println("Serialization Failure " + f.toString)
}
}
Related
I'm trying to move all of my implicits to a common object so I can use them in all of my DAO classes, but I cannot set it up correctly.
I have this so far:
package com.example.dao
import java.sql.Timestamp
import java.time.Instant
import play.api.db.slick.DatabaseConfigProvider
import play.api.db.slick.HasDatabaseConfigProvider
import slick.driver.JdbcProfile
object DbImplicits {
import driver.api._
implicit val dateColumnType = MappedColumnType.base[Instant, Timestamp](
i => Timestamp.from(i),
ts => {
ts.toInstant
}
)
}
I am getting this error:
not found: object driver
In your DAO it's taken from extended trait HasDatabaseConfigProvider.
Just put your conversation into trait then (trait DbImplicits) that require to be mixed-in together with or extends HasDatabaseConfigProvider.
trait DbImplicits { self: HasDatabaseConfigProvider[JdbcProfile] =>
import self.driver._
implicit val dateColumnType = MappedColumnType.base[Instant, Timestamp](
i => Timestamp.from(i),
ts => {
ts.toInstant
}
)
}
Later use it like this:
class MyClassUsingImplicits #Inject()(
protected val dbConfigProvider: DatabaseConfigProvider
) extends HasDatabaseConfigProvider[JdbcProfile]
with DbImplicits {
import driver._
// your further code comes here
}
All this is kind of specific to play-slick.
I also updated the original answer: https://stackoverflow.com/a/41437295/2239369
I'm using Play Scala 2.5, I would like to write functional tests for a controller class.
This is the signature of my controller class with a action I would like to test for example.
class MyController #Inject()(implicit context: ExecutionContext, val messagesApi: MessagesApi, val dao: Dao) extends Controller with I18nSupport {
def getMyData = Action.async { implicit request =>
dao.getMyData map { myData =>
Ok(views.html.render(myData))
}.recover {
// recover.
}
}
}
Here is my Dao class that is used in my controller's action :
import play.api.Configuration
class Dao #Inject()(val ws: WSClient, val configuration: Configuration) {
val myConfigData: List[MyConfigData] = { ... } // Get data from configuration
def getMyData(): Future[List[MyData]] = {
// use myConfigData and call web service (ws) to get some data
}
}
I would like to test my controller's action using different configuration in my Dao class.
I manage to do it and my test class is working but I would like some advices to improve the code.
package controllers
import play.core.server.Server
import org.scalatest._
import Inspectors._
import org.scalatestplus.play._
import play.api.routing.sird._
import play.api.libs.json._
import play.api.mvc._
import play.api.Play
import play.api.test._
import play.api.i18n.MessagesApi
import play.api.Configuration
import play.api.test.Helpers.{ OK, status, contentAsString }
import org.scalatest.concurrent.ScalaFutures
import scala.concurrent.Future
import scala.concurrent.Await
import scala.concurrent.duration._
import play.api.libs.json._
import play.api.mvc._
import scala.concurrent.ExecutionContext
import com.typesafe.config.ConfigFactory
import dao.{ Dao }
import akka.util.Timeout
class MyControllerFunctionalSpec extends PlaySpec with OneAppPerSuite with ScalaFutures {
implicit val messagesApi = app.injector.instanceOf[MessagesApi]
implicit val ec = app.injector.instanceOf[ExecutionContext]
implicit val timeout = Timeout(5 seconds)
trait OneConfig {
val config = ConfigFactory.parseString(""" my config """)
}
trait AnotherConfig {
val config = ConfigFactory.parseString(""" my other config """)
}
"MyController" should {
"get some data" in new OneConfig {
Server.withRouter() {
case GET(p"/url1/some/data") => Action {
Results.Ok(Json.arr(Json.obj("name" -> "data1")))
}
} { implicit port =>
WsTestClient.withClient { client =>
implicit val dao = new Dao(client, Configuration(config))
val myController = new MyController()
val result = myController.getMyData(FakeRequest())
status(result) must equal(OK)
//println(contentAsString(result))
}
}
}
}
}
It is working like this but I was guided by compilation error and by fixing them to get it to work.
I would like help or advices to do it the right way.
Any advice welcome.
I am using akka-http 1.0 and I would like to use a route defined as
def route: Route = path("") {
// start actor with requestContext
// call requestContext.complete(...) in actor with the result
}
How do I accomplish this?
Elaborating on #jrudolph's comment, the below code satisfies your requirements of dispatching RequestContext values to an Actor. Your question indicated that you wanted a new Actor for each request; however, the below code uses the same Actor for all requests which I think is a more efficient/likely use case. The Actor creation can always be moved inside handleRequest if needed.
First we need an Actor for processing a request to a response:
import akka.actor.Actor
import akka.http.scaladsl.server.{RequestContext, RouteResult}
import akka.http.scaladsl.model.HttpResponse
class RequestActor extends Actor {
//business logic - returns empty HttpResponse
def handleRequestMessage(requestContext : RequestContext) =
RouteResult.Complete(new HttpResponse())
override def receive = {
case reqContext : RequestContext =>
sender ! handleRequestMessage(reqContext)
}
}//end class RequestActor
Now create a utility function for querying the Actor:
import akka.actor.ActorRef
import scala.concurrent.Future
import akka.pattern.ask
object RequestActor {
val handleRequest : ActorRef => RequestContext => Future[RouteResult] =
(actorRef) =>
(requestContext) =>
ask(actorRef,reqContext).mapTo[RouteResult]
}
And all that is left to do is wire everything together into a service:
import akka.actor.{ActorSystem, Props}
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives.{get,path}
import akka.util.Timeout
object RouteActorTest extends App {
implicit val as = ActorSystem("RouteActorTest")
implicit val timeout = new Timeout(1000)
val sendRequestToActor : RequestContext => Future[RouteResult] =
RequestActor handleRequest (as actorOf Props[RequestActor])
val route = path("")(get(sendRequestToActor))
//rest of application...
}//end object RouteActorTest
you may try even better like:
package controllers
import akka.actor.{Actor, ActorSystem, Props}
import akka.stream.ActorMaterializer
import scala.concurrent.{Await, Future}
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import akka.util.Timeout
import akka.pattern.ask
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.io.StdIn
import akka.actor._
import akka.util.Timeout
case object Message
class TestActor(name:String) extends Actor {
def receive = {
case Message =>
sender ! "Testing Ask pattern Approach"
println(s"hello from $name")
case _ =>
println("that was unexpected")
}
}
object AskTest extends App {
implicit val system= ActorSystem("myactor")
implicit val material=ActorMaterializer()
// implicit val props=Props.empty
implicit val timeout = Timeout(5 seconds)
implicit val result =system.actorOf(Props(new TestActor("TestingName")),name = "Scala")
val future3:Future[String]= ask(result ,Message).mapTo[String]
val results = Await.result(future3, 2 seconds)
println(results)
}
Registration.scala
package model
import akka.actor.Actor
import spray.json._
import DefaultJsonProtocol._
case class Registration(
system: String,
identity: String)
object RegistrationProtocol extends DefaultJsonProtocol {
implicit val adsRegistrationFormat = jsonFormat2(Registration)
}
RegistrationService.scala
import akka.actor.{Props, ActorLogging, Actor}
import model.Registration
object RegistrationsService {
case class PostRegistrationMessage(registration: Registration)
def props(property: String) = Props(classOf[RegistrationsService], property)
}
class RegistrationsService(property: String) extends Actor with ActorLogging {
import RegistrationsService._
def receive = {
case PostRegistrationMessage(registration) => {
import model.RegistrationProtocol._
val json = registration.toJson
}
}
}
Can anyone help me understand why this is failing with compilation error "value toJson is not a member of model.Registration" and how to fix it. It if failing in the last line of the code above, which is "val json = registration.toJson"
You need to import implicit operations provided by spray in RegistrationService.scala too
import model.RegistrationProtocol._
import spray.json._
This code results in a compile error of could not find implicit value for parameter marshaller:
spray.httpx.marshalling.ToResponseMarshaller[List[akka.actor.ActorRef]].
I don't think the problem is the ActorRef, as changing this to .mapTo[List[String]] shows the same compile error
In general, it's somewhat confusing how spray does marshalling with all the implicits - is there a way to make this explicit e.g. ListProtocol.marshal(value)?
import akka.actor.Actor
import spray.http.HttpResponse
import spray.http.HttpRequest
import spray.http.Uri
import spray.http._
import spray.routing._
import HttpMethods._
import akka.actor.ActorRef
import akka.pattern.ask
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import scala.util.Failure
import spray.http.StatusCodes.InternalServerError
import spray.json.DefaultJsonProtocol
import spray.httpx.SprayJsonSupport._
import spray.httpx.marshalling._
import spray.http._
class HttpApi(val manager: ActorRef) extends HttpServiceActor {
def receive = runRoute {
path("nodes") {
get {
onComplete(manager.ask(NodeList())(3.seconds).mapTo[List[ActorRef]]) {
case Success(value) => {
// Compile error happens here
complete(value)
}
case Failure(ex) => {
complete(InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
}
}
}
}
Change this import
import spray.json.DefaultJsonProtocol
to
import spray.json.DefaultJsonProtocol._
That is, you want to import the implicits defined in that object, not the object itself.
Alternatively you can extend the trait to pick up the implicits:
class HttpApi(val manager: ActorRef) extends HttpServiceActor
with DefaultJsonProtocol {