CompleteWith directive in spray routing - scala

Hi i would like to use completeWith to use actor per request pattern in my test scala project, but i cannot get reference to this directive from spray. I tried using complete directive, and think that somewhere is magnet which will respond in manner that i want, but this:
pathPrefix("search") {
path("book" / "getAvailable") {
get {
respondWithMediaType(`application/json`) {
complete(instanceOf[Seq[Book]]) { completeFunction =>
Don't work. Do you know what directive in spray works like completeWith in Akka?

Answer is there is no completeWith in spray, i used akka http, since spray is no longer developed:
completeWith(instanceOf[Seq[BookApp]]) { completeFunction =>
system.actorOf(Props(new HandlerActor(bookService, authorService))) ! Handler.AllBooks(completeFunction)
}

Related

Migrating from Akka Http to Http4s - custom directives

I'm starting to learn Http4s, at work we may need to migrate Rest APIs implemented with Akka Http to Http4s.
As an example I can define a custom directive like this:
trait CustomDirectives {
def extractUUID: Directive1[UUID] = {
path(Segment).flatMap { maybeuuid =>
Try(UUID.fromString(maybeuuid)) match {
case Success(result) => provide(result)
case Failure(_) =>
complete(StatusCodes.BadRequest, s"Invalid uuid: $maybeuuid")
}
}
}
}
So everytime I want to extract an UUID and validate it I can use this directive.
We have other custom directives to make some processing on headers, and many more.
Is there anything similar to akka custom directives but in Http4s?
This is described in Handling Path Parameters section of the documentation
// standard Scala extractor, matching String against UUID
object UUIDVar {
def unapply(maybeuuid: String): Option[UUID] =
Try(UUID.fromString(maybeuuid)).toOption
}
val usersService = HttpRoutes.of[IO] {
// just use extractor in pattern matching
case GET -> Root / "users" / UUIDVar(uuid) =>
Ok(useUUIDForSth(uuid))
}
However, personally, I find it easier to describe endpoints with libraries like Tapir or Endpoint4s since their DSL seem more intuitive to me and I am not coupling my code with a particular implementation.

How do I serialise/deserialise protobuf in akka http?

I'm using Akka Http 2.4.7. For Json (that use json4s), serialisation is something like this:
entity(as[CaseClass]) { serialisedCaseClass => .... }
for deserialising, I just return a case class object.
I wonder is there a similar way for protobuf?
No, there isn't anything available out of the box (but I agree it would be nice to have). You currently need to build your own custom unmarshaller to do that (as of akka-http 2.4.11).
You can build your own unmarshaller based on an existing protobuf definition. Here's a stub how to do it:
case class Person(...)
implicit val personUnmarshaller: FromEntityUnmarshaller[Person] =
PredefinedFromEntityUnmarshallers.byteArrayUnmarshaller map { bytes =>
// bytes is an Array[Byte]
// call protobuf to do the unmarshalling
}
and then import this definition in your route.

Akka IO and TestActorRef

If I need to write an integration test involving HTTP request via spray-can, how can I make sure that spray-can is using CallingThreadDispatcher?
Currently the following actor will print None
class Processor extends Actor {
override def receive = {
case Msg(n) =>
val response = (IO(Http) ? HttpRequest(GET, Uri("http://www.google.com"))).mapTo[HttpResponse]
println(response.value)
}
}
How can I make sure that the request is being performed on the same thread as the test (resulting in a synchronous request)?
It seems like strange way to do integration internal-testing as you don't mock the "Google", so is more like integration external-testing and synchronous TestActorRef doesn't much fit here. The requirement to control threads inside spray is also pretty tricky. However, if you really need that for http-request - it's possible. In general case, you have to setup several dispatchers in your application.conf:
"manager-dispatcher" (from Http.scala) to dispatch your IO(Http) ? req
"host-connector-dispatcher" to use it by HttpHostConnector(or ProxyHttpHostConnector) which actually dispatch your request
"settings-group-dispatcher" for Http.Connect
They all are decribed in Configuration Section of spray documentation. And they all are pointing to "akka.actor.default-dispatcher" (see Dispatchers), so you can change all of them by changing this one.
The problem here is that calling thread is not guaranteed to be your thread actually, so it will NOT help much with your tests. Just imagine if some of actors registers some handler, responding to your message:
//somewhere in spray...
case r#Request => registerHandler(() => {
...
sender ! response
})
The response may be sent from another thread, so response.value may still be None in current. Actually, the response will be sent from the listening thread of underlying socket library, indepently from your test's thread. Simply saying, request may be sent in one (your) thread, but the response is received in another.
If you really really need to block here, I would recommend you to move such code samples (like IO(Http) ? HttpRequest) out and mock them in any convinient way inside your tests. Smtng like that:
trait AskGoogle {
def okeyGoogle = IO(Http) ? HttpRequest(GET, Uri("http://www.google.com"))).mapTo[HttpResponse]
}
trait AskGoogleMock extends AskGoogle {
def okeyGoogle = Await.result(super.okeyGoogle, timeout)
}
class Processor extends Actor with AskGoogle {
override def receive = {
case Msg(n) =>
val response = okeyGoogle
println(response.value)
}
}
val realActor = system.actorOf(Props[Processor])
val mockedActor = TestActorRef[Processor with AskGoogleMock]
By the way, you can mock IO(HTTP) with another TestActorRef to the custom actor, which will do the outside requests for you - it should require minimal code changes if you have a big project.

How to use NoRoutee as catch-all case for custom routing logic?

I am having trouble specifying some custom routing logic for my Akka Router. Here is what I have so far:
class OrderRoutingLogic extends RoutingLogic {
private val markets = mutable.Map.empty[Security, ActorRef]
def select(message: Any, routees: IndexedSeq[Routee]): Routee = {
message match {
case order: Order => ActorRefRoutee(markets(order.security))
case _ => NoRoutee // will send to DeadLetters!
}
}
IntelliJ IDEA is telling me that I have not specified the select(message: Any, routees: IndexedSeq[Routee]): Routee and I do not understand why. How to use custom Router in Akka 2.3? has an answer that uses akka.routing.NoRoutee (although without pattern matching). What have I done wrong?
This is because the routees parameter type is not correct: it should be immutable.IndexedSeq[Routee] and not IndexedSeq[Routee]. I agree this is tricky!

Scala remote actors

Are there any guides or tutorials which explain the possibility to use scala actors remotely? All I have found until now is one example (without comments) but that's hardly enough.
I have written an article with an example app to explain the use of Remote Actors a bit some time ago.
Well, it has no comments inside the code (maybe you even meant that article), but there are explanations below the code.
Just be careful to send messages that are serializable (case classes and case objects are!) and be sure the opposite side can create the class.
Watch out for custom ClassLoaders or missing JARs in you classpaths.
None of which I am aware. It's pretty much a "hack your way through the jungle" approach. Judging from the API though, things should work pretty much the same as regular actors, for which there exist one or two tutorials (as well as a few books now).
If you do make use of remote actors, we (the community) would certainly welcome such a tutorial from an experienced user!
The Akka framework has remote actors. The API is pretty similar to regular scala actors.
They provide some level of automatic clustering as well, but it's not complete.
recently there was a guide added on the front page of www.scala-lang.org, here is the link
http://www.scala-lang.org/docu/files/actors-api/actors_api_guide.html#
Maybe this is a necropost but I was looking for all over and could not find much. Hopefully this will help someone.
I am running Mac OS 10.6.8 and Scala 2.9.0.1. I had problems getting the canonical remote actors example running. I ended up with the following code.
Note: The clear method is just to prevent messages from piling up. It's not critical to the example. Likewise the calls to Thread.sleep are just to make it easier to see what is going on at runtime.
Compile it, then in separate shell instances do:
$> scala Ping
and
$> scala Pong
in any order. You can experiment by killing one of them at a time and tracing the code.
import scala.actors._
import scala.actors.Actor._
import scala.actors.remote._
import scala.actors.remote.RemoteActor._
/** #author Connor Doyle */
// Remote messages must be serializable.
// The easist way to do this is to wrap
// them with a case class
case class Message(text: String)
abstract class PingPongActor extends Actor with App {
val pingPort = 9000
val pongPort = 9001
val delay = 1000
classLoader = getClass().getClassLoader() // hack!
start
// this method consumes all pending messages
// the library should have implemented an atomic
// receiveAndClear operation
def clear: Unit = receiveWithin(0) {
case TIMEOUT => ()
case _ => clear
}
}
object Ping extends PingPongActor {
// result of select already lazy, but explicit lazy conveys
// semantics clearly
lazy val pong = select(Node("localhost", pongPort), 'pong)
def act = {
alive(pingPort)
register('ping, self)
loop {
pong ! Message("ping")
receiveWithin(delay * 2) {
case Message(text: String) => {
clear
println("received: "+text)
Thread.sleep(delay) // wait a while
}
case TIMEOUT => println("ping: timed out!")
}
}
}
}
object Pong extends PingPongActor {
lazy val ping = select(Node("localhost", pingPort), 'ping)
def act = {
alive(pongPort)
register('pong, self)
loop {
receiveWithin(delay * 2) {
case Message(text: String) => {
println("received: "+text)
Thread.sleep(delay) // wait a while
clear
ping ! Message("pong")
}
case TIMEOUT => println("pong: timed out")
}
}
}
}
Cheers!