My Akka actor system has some tests to verify message content
myEventActor.expectMsgPF() {
verifyEventPF(id)
}
def verifyEventPF(id: String): PartialFunction[Any, Any] = {
case e : MyEvent if e.id == id => e.otherID
}
For example, we use this partial function to check that the id on the event is correct.
But how do I go about getting the result of the partial function e.g. if I want to achieve the following
myEventActor.expectMsgPF() {
var otherID = verifyEventPF(id) // How do I achieve this?
}
Is this possible?
(I am aware I don't need to use a partial function here and could use Akka TestProbe.receiveOne() but I'd like to understand how this is possible)
Calling verfiyEventPF(id) returns a PartialFunction[Any, Any]. You have to also call the returned function (giving it input, of course): verifyEventPF(id)(event).
This is the same as the following:
val getId: PartialFunction[Any, Any] = verifyEventPF(id)
getId(event)
If you're concerned about the partial funciton not being defined for a particular input, you can check if the function is defined for a given value:
if (getId.isDefinedAt(event)) {
getId(event)
}
Or you can lift the partial function into a total function that returns an Option:
val totalGetId: (Any => Option[Any]) = getId.lift
val result: Option[Any] = totalGetId(event)
Related
I'm building an API, that takes in a variable path parameter, or dynamic part of the route, as the play documentation would specify it.
I would like to validate this as to give the client a proper response.
I have the following route setup
GET /:dynamic/all controller.method(dynamic: String)
The dynamic param for the method is used across the API, for multiple methods, so i would like to get some kind of global validation/whitelist of acceptable strings. (eg: "hello"/"hi" would be accepted, and "noooway" would not be accepted, and i would return a 404 not found as response.
I would preferably like my controller method to not contain any validation so that this would be true:
def method(dynamic: String): Action[AnyContent] = Action.async { _ =>
//I already know "dynamic" is valid here.
Future.successful(Ok(Json.toJson(Map("status" -> "OK"))))
}
Instead of: (excuse my javaisc-psuedo-code)
def method(dynamic: String): Action[AnyContent] = Action.async { _ =>
val valid = Helper.validate(dynamic)
if (!valid) return some result/response else
Future.successful(Ok(Json.toJson(Map("status" -> "OK"))))
}
Play allows you to do this by different ways.
1. PathBindable
You can implement a PathBindable[T] for any type T, so that your value extracted from the path of the request is not a simple String but a T.
If you are ready to change the type of dynamic (which would make sense, since it is not supposed to be just any string but a valid one), you could do the following:
case class Validated(str: String) {
assert(Helper.validate(str))
}
object Validated {
implicit val pathBindable = new PathBindable[Validated] {
val string = implicitly[PathBindable[String]]
override def bind(key: String, value: String): Either[String, Validated] =
string.bind(key, value). // bind as if it were a string
right.filter(Helper.validate).getOrElse(Left("Invalid input")). // filter using your validation function, and give error message
right.map(Validated(_)) // encapsulate in your new type
override def unbind(key: String, value: Validated): String =
string.unbind(key, value.str) //unbind as if it were a string
}
}
Note that you need to implement unbind for reverse routing (get a path for a given action call).
Now, you just need to replace String in your router and in your controller by your.package.Validated.
GET /:dynamic/all controller.method(dynamic: your.package.Validated)
NB: if you want to use the simple name of your class, you need to import it in your build.sbt:
(project in file(".").
enablePlugins(PlayScala).
settings(routesImport += "your.package.Validated")
2. Action Composition
You can also implement an action filter to be used whenever your input needs to be validated:
case class ValidatedAction(input: String) extends ActionFilter[Request] {
override protected def filter[A](request: Request[A]): Future[Option[Result]] = Future.successful{
if (Helper.validate(input)) None else Some(BadRequest("Invalid input"))
}
}
def method(dynamic: String) = (Action andThen ValidatedAction(dynamic)).async {
Future.successful(Ok)
}
The code inside the async block will be executed only if the filter method returns None, otherwise, it will return the specified Result (here, BadRequest("Invalid input").
In order to be able to handle large amounts of different request types I created a .proto file like this:
message Message
{
string typeId = 1;
bytes message = 2;
}
I added the typeId so that one knows what actual protobuf bytes represents. (Self-describing)
Now my problem is handling that different "concrete types" in an elegant way. (Note: All works fine if I simple use a switch-case-like approach!)
I thought about a solution like this:
1) Have a trait the different handlers have to implement, e.g.:
trait Handler[T]
{
def handle(req: T): Any
}
object TestHandler extends Handler[Test]
{
override def handle(req: Test): String =
{
s"A success, $req has been handled by TestHandler
}
}
object OtherHandler extends Handler[Other]
{
override def handle(req: Other): String =
{
s"A success, $req has been handled by OtherHandler
}
}
2) provide some kind of registry to query the right handler for a given message:
val handlers = Map(
Test -> TestHandler,
Other -> OtherHandler
)
3) If a request comes in it identifies itself, so we need another Mapper:
val reqMapper = Map(
"Test" -> Test
"Other" -> Other
)
4) If a request comes in, handle it:
val request ...
// Determine the requestType
val requestType = reqMapper(request.type)
// Find the correct handler for the requestType
val handler = handlers(requestType)
// Parse the actual request
val actualRequest = requestType.parse(...) // type of actualRequest can only be Test or Other in our little example
Now, until here everything looks fine and dandy, but then this line breaks my whole world:
handler.handle(actualRequest)
It leads to:
type mismatch; found : com.trueaccord.scalapb.GeneratedMessage with Product with com.trueaccord.scalapb.Message[_ >: tld.test.proto.Message.Test with tld.test.proto.Message.Other <: com.trueaccord.scalapb.GeneratedMessage with Product] with com.trueaccord.lenses.Updatable[_ >: tld.test.proto.Message.Other with tld.test.proto.Message.Test <: com.trueaccord.scalapb.GeneratedMessage with Product]{def companion: Serializable} required: _1
As far as I understand - PLEASE CORRECT ME HERE IF AM WRONG - the compiler cannot be sure here, that actualRequest is "handable" by a handler. That means it lacks the knowledge that the actualRequest is definitely somewhere in that mapper AND ALSO that there is a handler for it.
It's basically implicit knowledge a human would get, but the compiler cannot infer.
So, that being said, how can I overcome that situation elegantly?
your types are lost when you use a normal Map. for eg
object Test{}
object Other{}
val reqMapper = Map("Test" -> Test,"Other" -> Other)
reqMapper("Test")
res0: Object = Test$#5bf0fe62 // the type is lost here and is set to java.lang.Object
the most idomatic way to approach this is to use pattern matching
request match {
case x: Test => TestHandler(x)
case x: Other => OtherHandler(x)
case _ => throw new IllegalArgumentException("not supported")
}
if you still want to use Maps to store your type to handler relation consider HMap provided by Shapeless here
Heterogenous maps
Shapeless provides a heterogenous map which supports an arbitrary
relation between the key type and the corresponding value type,
I settled for this solution for now (basically thesamet's, a bit adapted for my particular use-case)
trait Handler[T <: GeneratedMessage with Message[T], R]
{
implicit val cmp: GeneratedMessageCompanion[T]
def handle(bytes: ByteString): R = {
val msg: T = cmp.parseFrom(bytes.newInput())
handler(msg)
}
def apply(t: T): R
}
object Test extends Handler[Test, String]
{
override def apply(t: Test): String = s"$t received and handled"
override implicit val cmp: GeneratedMessageCompanion[Test] = Test.messageCompanion
}
One trick you can use is to capture the companion object as an implicit, and combine the parsing and handling in a single function where the type is available to the compiler:
case class Handler[T <: GeneratedMessage with Message[T]](handler: T => Unit)(implicit cmp: GeneratedMessageCompanion[T]) {
def handle(bytes: ByteString): Unit = {
val msg: T = cmp.parseFrom(bytes.newInputStream)
handler(t)
}
}
val handlers: Map[String, Handler[_]] = Map(
"X" -> Handler((x: X) => Unit),
"Y" -> Handler((x: Y) => Unit)
)
// To handle the request:
handlers(request.typeId).handle(request.message)
Also, take a look at any.proto which defines a structure very similar to your Message. It wouldn't solve your problem, but you can take advantage of it's pack and unpack methods.
I am trying to select from a few tables and put their results in an Object. I want to do this because those are dictionaries and I want all of them at startup.
This is what I have right now:
Controller:
def getDictionaries = Action.async { implicit request =>
Future.successful(Ok(Json.toJson(dictionaryService.getDictionaries)))
}
DictionaryService:
override def getDictionaries : Dictionaries = {
val currencies: Future[Seq[Currency]] = dictionaryDao.getCurrencies
val propertyTypes: Future[Seq[PropertyType]] = dictionaryDao.getPropertyTypes
Dictionaries(
currencies.result(Duration(10L, TimeUnit.SECONDS)),
propertyTypes.result(Duration(10L, TimeUnit.SECONDS))
)
}
DictionaryDAO:
override def getCurrencies: Future[Seq[Currency]] = {
db.run(slickCurrencies.result)
}
... getPropertyTypes ...
Dictionaries case class and companion object
case class Dictionaries (currencies: Seq[Currency], propertyTypes: Seq[PropertyType])
object Dictionaries {
implicit val jsonFormat = Json.format[Dictionaries]
}
I am not very proud of currencies.result(Duration(10L, TimeUnit.SECONDS)) but I am not sure what I should retrieve from this function so that I can easily transform it to JSON. Also this line of code is still not working because the compiler is telling me to use Await object instead.
Also for PropertyType I need to do the same thing as for currencies.
What is the best way to obtain the desired result?
I am in the learning phase so I don't get much of this.
LATER EDIT: the flow is: request -> getDictionaries -> dictionaryService.getDictionaries -> dictionaryDAO.getCurrencies&PropertyTypes
It seems to me that I need to get them in a synchronized way.
The purpose of this is to not create a request for each type of dictionary. If I have 10 dictionaries, I want to get all of them in one request.
Later EDIT 2
This is my working example which does not look very well:
Controller:
def getDictionaries = Action.async { implicit request =>
dictionaryService.getDictionaries.map {
dictionaries => Ok(Json.toJson(dictionaries))
}
}
DictionaryService:
override def getDictionaries : Future[Dictionaries] = {
dictionaryDao.getCurrencies.flatMap { currencies =>
dictionaryDao.getPropertyTypes.flatMap { propertyTypes =>
Future.successful(Dictionaries(
currencies,
propertyTypes
))
}
}
}
Your method getDictionaries should return Future[Dictionaries]:
override def getDictionaries : Future[Dictionaries] = {
val futureCurrencies = dictionaryDao.getCurrencies
futureCurrencies map { currencies => // currencies is here Seq[Currency]
Dictionaries(
currencies,
List(PropertyType(1, "x"))
)
}
And your controller method:
def getDictionaries = Action.async { implicit request =>
val futureDictionaries = dictionaryService.getDictionaries
futureDictionaries map { dictionaries =>
Ok(Json.toJson(dictionaries))
}
}
EXPLANATION: You have to use Futures all the way bottom up if you want your actions to be really asynchronous. You'll have to learn how to compose them with map, flatMap and for comprehensions. I didn't find any gentle and short introduction for that, you'll have to search it by yourself. Basically, when you map over Future, you are transforming it's(successful) result, and thus getting another Future. With flatMap you can have some ordering of futures, like: when f1 finishes, start f2 etc...
I'm trying to implement JSON-RPC server in Scala and want to mark my remote methods with annotations. Also it will be nice if method itself should not be worried about input parameters validation, so I want to put this validations inside annotation. I ended up with this:
class RPCMethod(validators: (String, PartialFunction[Any, Boolean])*) extends StaticAnnotation
And remote method is annotated like this:
#RPCMethod(
"name" -> {
case x: String => x == "Andrey"
}
)
Now I'm trying to extract the {case ...} part and eval it to a function to check inbound value. But all I could get with reflection is a Tree:
val annotation = method.annotations.find(_.tree.tpe == typeOf[RPCMethod]).get
val validators = annotation.tree.children.tail
if (validators.isEmpty) {
return true
} else {
validators.head match {
case q"scala.this.Predef.ArrowAssoc[$_](${name: String}).->[$_]($func)" => // Don't know how to eval 'func'
case _ => throw some error
}
}
So how can I get a valid function object from this Tree?
This is somewhat of a theoretical question but something I might want to do. Is it possible to return multiple data data types from a Scala function but limit the types that are allowed? I know I can return one type by specifying it, or I can essentially allow any data type by not specifying the return type, but I would like to return 1 of 3 particular data types to preserve a little bit of type safety. Is there a way to write an 'or' in the return type like:
def myFunc(input:String): [Int || String] = { ...}
The main context for this is trying to write universal data loading script. Some of my users use Spark, some Scalding, and who knows what will be next. I want my users to be able to use a generic loading script that might return a RichPipe, RDD, or some other data format depending on the framework they are using, but I don't want to throw type safety completely out the window.
You can use the Either type provided by the Scala Library.
def myFunc(input:String): Either[Int, String] = {
if (...)
Left(42) // return an Int
else
Right("Hello, world") // return a String
}
You can use more than two types by nesting, for instance Either[A,Either[B,C]].
As already noted in comments you'd better use Either for this task, but if you really want it, you can use implicits
object IntOrString {
implicit def fromInt(i: Int): IntOrString = new IntOrString(None, Some(i))
implicit def fromString(s: String): IntOrString = new IntOrString(Some(s), None)
}
case class IntOrString(str: Option[String], int: Option[Int])
implicit def IntOrStringToInt(v: IntOrString): Int = v.int.get
implicit def IntOrStringToStr(v: IntOrString): String = v.str.get
def myFunc(input:String): IntOrString = {
if(input.isEmpty) {
1
} else {
"test"
}
}
val i: Int = myFunc("")
val s: String = myFunc("123")
//exception
val ex: Int = myFunc("123")
I'd make the typing by the user less implicit and more explicit. Here are three examples:
def loadInt(input: String): Int = { ... }
def loadString(input: String): String = { ... }
That's nice and simple. Alternatively, we can have a function that returns the appropriate curried function using an implicit context:
def loader[T]()(implicit context: String): String => T = {
context match {
case "RDD" => loadInt _ // or loadString _
}
}
Then the user would:
implicit val context: String = "RDD" // simple example
val loader: String => Int = loader()
loader(input)
Alternatively, can turn it into an explicit parameter:
val loader: String => Int = loader("RDD")