Returning a JSON array in Akka Http - scala

I have an Akka HTTP server with routing defined like this:
case class FooResults(results: Seq[Tuple2[String, Tuple2[Double, Double]]])
object MainApp extends App with JsonSupport {
...
lazy val routes: Route =
pathPrefix("foo") {
pathEnd {
get {
entity(as[String]) { str =>
val results =
(fooActor ? Foo(str)).mapTo[FooResults]
complete(results)
}
}
}
}
...
And in the class I have injected the implicit json support:
trait JsonSupport extends SprayJsonSupport {
import DefaultJsonProtocol._
implicit val userFormat = jsonFormat1(FooResults)
}
Somehow sbt still reports with
Type mismatch - FooResults with ToResponseMashallable
Anyone had similar problems? Thanks!

I figured out myself. It was because there're two SprayJsonSupport classes in my project:
import spray.httpx.SprayJsonSupport
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
Now obviously the latter is the correct one. Guess along the way since both Scala and Akka are evolving (fast), sometimes it becomes confusing with the namespaces and classes.

Related

having problems calling a method from controller in scala

i'm using playframework with scala and slick.
in my dto (dao) I do this:
class processDTO #Inject() (protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile]
with ProcessTemplatesComponent {
import driver.api._
private val processTemplates = TableQuery[ProcessTemplates]
def getAll(): Future[Seq[ProcessTemplatesModel]] = db.run { processTemplates.to[Seq].result }
}
and in controller I do this:
#Singleton
class ProcessesController #Inject() (processDTO: processDTO, actionBuilder: ActionBuilders) extends Controller{
def getProcesses() = actionBuilder.DynamicAction(name = "pureLuck").defaultHandler() {
request =>
processDTO.getAll().map(_.map(result => {
}))
.map(result => Ok(Json.toJson(result)))
}
}
and now I got this error
First of all you need to import execution context. PlayFramework has his own context. Add this import play.api.libs.concurrent.Execution.Implicits._ Play documentation
Also you need to return something from this code of block:
_.map(result => {})
Currently you return Unit which can't be transformed to json.
The error tells you what you need to know. Adding import scala.concurrent.ExecutionContext.Implicits.global at the top of your file should fix it.
One issue is the lack of execution context as other had mentioned.
As of your json error, you need to implement a Write converter to transform your ProcessTemplatesModel objects into json.

How to mock using external call in Akka Actor using ScalaTest

I am new to entire ecosystem including Scala, Akka and ScalaTest
I am working on a problem where my Actor gives call to external system.
case object LogProcessRequest
class LProcessor extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
LogReaderDisruptor main(Array())
}
}
The LogReaderDisruptor main(Array()) is a Java class that does many other things.
The test I have currently looks like
class LProcessorSpec extends UnitTestSpec("testSystem") {
"A mocked log processor" should {
"be called" in {
val logProcessorActor = system.actorOf(Props[LProcessor])
logProcessorActor ! LogProcessRequest
}
}
}
where UnitTestSpec looks like (and inspired from here)
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.matchers.MustMatchers
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
abstract class UnitTestSpec(name: String)
extends TestKit(ActorSystem(name))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll() {
system.shutdown()
}
}
Question
How can I mock the call to LogReaderDisruptor main(Array()) and verify that it was called?
I am coming from Java, JUnit, Mockito land and something that I would have done here would be
doNothing().when(logReaderDisruptor).main(Matchers.<String>anyVararg())
verify(logReaderDisruptor, times(1)).main(Matchers.<String>anyVararg())
I am not sure how to translate that with ScalaTest here.
Also, This code may not be idiomatic, since I am very new and learning
There are a few ways to do this. The kind of OO way is to wrap logDisrupter as an object and pass it in. I would set up a companion object to instantiate the actor. Something like below. Then you can pass alternate implementation. You can also achieve a similar approach by using traits and mixing in an alternative logDisrupter only as needed.
object LProcessor {
def props(logDisrupter : LogDisrupter) = Props(new LProcessor(logDisrupter))
}
class LProcessor(logDisrupter : LogDisrupter) extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
logDisrupter.doSomething();
}
}
Then instatiate as
val logProcessorActor = system.actorOf(LProcessor.props(logDisrupter))

Getting implicit val from the companion object

I'm playing with Scala Spray. I enjoy working with it but can't figure out one thing.
This code compiles fine:
import spray.http.MediaTypes._
import spray.routing.HttpService
import spray.json.{DefaultJsonProtocol, _}
import spray.httpx.SprayJsonSupport._
trait StatusService extends HttpService {
case class StatusResponse(status: String)
object StatusResponseProtocol extends DefaultJsonProtocol {
implicit val statusResponse = jsonFormat1(StatusResponse)
}
import StatusResponseProtocol._
val statusRoute =
path("status") {
get {
respondWithMediaType(`application/json`) {
complete {
StatusResponse("OK")
}
}
}
}
}
But it doesn't compile (can't find json serializer) when I move case class & protocol to the companion object.
trait StatusService extends HttpService {
import StatusResponseProtocol._
val statusRoute =
path("status") {
get {
respondWithMediaType(`application/json`) {
complete {
StatusResponse("OK")
}
}
}
}
}
object StatusService {
case class StatusResponse(status: String)
object StatusResponseProtocol extends DefaultJsonProtocol {
implicit val statusResponse = jsonFormat1(StatusResponse)
}
}
I do not understand why..
I think the problem might be in your import statement. If you import from a companion object, it should be done like this:
trait StatusService extends HttpService {
import StatusService.StatusResponseProtocol._
The rest of the code doesn't have to be changed.
I think I've had pretty much the same problem. Try replacing:
import spray.httpx.SprayJsonSupport._
with
import spray.json._
It worked for me.
I noticed today (in another context, not Spray) that providing a type for the implicit val in a companion object made it visible.
So, I'm thinking whether this would make it work in your case:
implicit val statusResponse: RootJsonFormat[StatusResponse] = jsonFormat1(StatusResponse)
Note: I'm not sure of the type I added - it may not be what jsonFormat1 returns. Also, the Spray.json documentation does not use types. Anyways, if someone has the time to try this out, I'd be interested to know..

Spray won't convert my case class to json and expect a spray.httpx.marshalling.ToResponseMarshallable

I'm trying to reprocude this or this, but I keep getting an error I am not able to fix...
First of all, here are my dependencies:
compile 'io.spray:spray-can_2.11:1.3.1'
compile 'io.spray:spray-routing_2.11:1.3.1',
compile 'io.spray:spray-json_2.11:1.2.6'
Now what I'm trying to do is:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import spray.json._
import DefaultJsonProtocol._
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
With:
case object MasterJsonProtocol extends DefaultJsonProtocol with SprayJsonSupport {
import spray.json._
case class Answer(code: Int, content: String)
implicit val anwserFormat: JsonFormat[Answer] = jsonFormat2(Answer)
}
Now I get this error:
Error:(42, 19) type mismatch;
found : MasterJsonProtocol.Answer
required: spray.httpx.marshalling.ToResponseMarshallable
Answer(1, "test")
^
I tried a lot of things but can't manage to make it works.
I tried with
Answer(1, "test").toJson
Answer(1, "test").toJson.asJsObject
Finally what I did was
complete {
Answer(1, "test").toJson.compactPrint
}
This works but it is sent to the client as Content-Type: text/plain when I need application/json.
Anyone see what the problem is here?
Edit: I added a sample project on github https://github.com/ydemartino/spray-test
Move your model outside of the json protocol and make it a regular object (not a case object)
case class Answer(code: Int, content: String)
object MasterJsonProtocol extends DefaultJsonProtocol {
implicit val anwserFormat = jsonFormat2(Answer)
}
Edit
Also clean up your imports:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
I created a pull request to fix your problem: https://github.com/ydemartino/spray-test/pull/1
The json protocol object has to be declared before it can be used implicitly. I'm not wholly sure why the compiler can't figure it out, but moving the object declaration to the top fixed it.
For your actual project make sure to declare packages in each file then use those packages to in the import statements.
In my case the name of the unresolvable implicit format instance conflicted with a local definition, so it got shadowed. The compiler was graciously silent about that. Only discovered that by accident after hours of head-banging.

DefaultMarshallers missing with scala and spray-routing

I'm new to Scala, and trying to write a little REST API.
Here is my route definition :
package com.example
import akka.actor.Actor
import com.example.core.control.CrudController
import spray.routing._
class ServiceActor extends Actor with Service {
def actorRefFactory = context
def receive = runRoute(routes)
}
trait Service extends HttpService {
val crudController = new CrudController()
val routes = {
path("ads" / IntNumber) { id =>
get {
complete(
crudController.getFromElasticSearch(id)
)
}
}
}
}
and here is my controller
package com.example.core.control
import com.example._
import org.elasticsearch.action.search.SearchResponse
import scala.concurrent._
import ExecutionContext.Implicits.global
class CrudController extends elastic4s
{
def getFromElasticSearch (id:Integer) : Future[String] = {
val result: Future[SearchResponse] = get
result onFailure {
case t: Throwable => println("An error has occured: " + t)
}
result map { response =>
response.toString
}
}
}
When I try to run this code, I got the following exception :
Error:(22, 58) could not find implicit value for parameter marshaller: spray.httpx.marshalling.ToResponseMarshaller[scala.concurrent.Future[String]]
crudController.getFromElasticSearch(id)
I understand quite well this error, spray needs an implicit marshaller in order to marshall my Future[String] Object. But I'm a little bit confused because in the documentation we can read
Scala compiler will look for an in-scope implicit Marshaller for your type to do the job of converting your custom object to a representation accepted by the client. spray comes with the following marshallers already defined (as implicit objects in the DefaultMarshallers trait) Source https://github.com/spray/spray/wiki/Marshalling-and-Unmarshalling
The required marshallers in my case belongs to DefaultMarshallers, I should not have to implicit him by myself.. Should I ?