Play2 - validation of dynamic part of route(s) - scala

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").

Related

Routes parameter transformation throws on parsing failure String -> Int

Router config using Play parameter transformation:
GET /search controllers.Search.query(page: Option[Int])
while search?page=123 works as expected, visiting search?page=asd throws
We're using our own wrappers to contain failures Try { param.toInt }.toOption but is there another way to cleanly contain parse exceptions in routes configuration or Action block?
Also: does anyone see parameter transformation is represented in the Play source?
Change Option[Int] to Option[String]
inside the action
def search(param: Option[String]) = Action { req =>
val page = param.flatMap { Try { _.toInt }.toOption }
doSomething(page).map { Ok(_) }
}
if you use Option[Int] parsing of query params happens at play routes level and play will handle the exception for you and you will not have control on it. So use Option[String] and parse String in the Action to anything you would like to see
use withParamAction to avoid duplicate code
def withParamAction(f: Option[Int] => Request[AnyContent] => Result)(paramName: String = "page"):Result = Action { req =>
f(req.getQueryString(paramName).flatMap { Try { _.toInt }.toOption })(req)
}
extending or writing a custom int bindable should be what is required in your situation customIntBinder
If you want to parse Query string to Complex objects use QueryStringBindlein play. Here is the example code from play docs.
https://www.playframework.com/documentation/2.5.x/ScalaRequestBinders#QueryStringBindable

Scala: getting result of PartialFunction

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)

Add Prefix to Reverse Routes

I am looking for a way to add a prefix to all reverse routes of an html template without wrapping them all in function applications. I planned to use an html base tag but the reverse routes all start with a slash and therefore are relative to the host of the base tag rather than the full URL. Is there any feature I might be missing for a more robust solution?
rename routes to context.routes
create empty routes file
add to routes the line:
-> /context context.Routes
To accomplish something like this, to facilitate communication to my app of my app's own URLs in a DRY way, to propagate query parameters debug settings used in development through internal links, and also because I find Play's reverse router to be super ugly, I created a URL abstraction I like a lot better. It works like this:
1) I created a type to represent URLs symbolically:
/** Represents a URL that can possibly be a webpage */
sealed trait PageUrl
case class SectionUrl(slug: String) extends PageUrl
case class StaticPageUrl(slug: String) extends PageUrl
case class ExternalUrl(url: String) extends PageUrl
2) I created a class for resolving these objects into full URLs:
/** Wrapper to propagate request override flags to internal links */
case class UrlWrapper(params: Seq[(String, String)]) {
def apply(url: PageUrl, additionalParams: Seq[(String, String)] = Seq.empty): String = {
url match {
case SectionUrl(slug) => urlAndParams(routes.PageRendererController.showSectionPage(slug).url)
case StaticPageUrl(slug) => urlAndParams(routes.PageRendererController.showStaticPage(slug).url)
case ExternalUrl(u) => u
}
}
def urlAndParams(url: String, additionalParams: Seq[(String, String)] = Seq.empty): String = {
def urlEncode = (u: String) => java.net.URLEncoder.encode(u, "UTF-8")
val formattedParams = (queryParams ++ additionalParams).map{ case (key, value) => s"$key=${urlEncode(value)}" }.mkString("&")
val paramOption = if (formattedParams.nonEmpty) Some(formattedParams) else None
(Seq(url) ++ paramOption).mkString(if (url.indexOf("?") > 0) "&" else "?")
}
}
You could easily modify this to provide a prefix always by default, or upon request via some other method.
3) In a trait/class that all my views extend, I declare an implicit field of type UrlWrapper, that will be available to my templates, so I can do:
#(option1: String, urlParam: PageUrl)(implicit url: UrlWrapper)
...
My link
...
As a bonus, since my pages all correspond to models in my app, I added to UrlWrapper additional methods for converting model objects to resolved URLs:
case class UrlWrapper(...) {
...
def forSection(section: Section, additionalParams: Seq[(String, String)] = Seq.empty): String = {
apply(SectionUrl(section.slug), additionalParams)
}
def forStaticPage(staticPage: StaticPage, additionalParams: Seq[(String, String)] = Seq.empty): String = {
apply(StaticPageUrl(staticPage.slug), additionalParams)
}
}

Function chaining in scala

I can't seem to figure out how to chain together these functions, any help or advice would be appreciated.
// Generic approach to adding flags to a command string
trait UpdateCommandString {
def update[T](option: Option[T], flagName: String)(implicit command: String): String = {
if (option.isEmpty)
command
else if (option.get.isInstanceOf[Boolean]) {
if (option.get.asInstanceOf[Boolean])
s"$command $flagName"
command
} else
s"$command $flagName ${option.get.asInstanceOf[String]}"
}
}
// One example of flags (the program I'm using has literally 50+ flags
// so there will be a number of case classes that group them into related
// sets)
case class Flags(cache: Option[String] = None,
errorlog: Option[String] = None,
accesslog: Option[String] = None,
verbose: Option[Boolean] = Some(false),
auth: Option[Boolean] = Some(false)) extends UpdateCommandString {
def applyToCommand(implicit command: String): String = {
// These seem to apply separately, but I want to chain
// them together!
update(cache, "-cache")
update(errorlog, "-error")
update(accesslog, "-access")
update(auth, "-do-auth")
}
}
// An example of what I'm trying to do
// Given a base command string and a bunch of case classes to apply
// to that string, I'd like to be able to call applyToCommand and
// get back the modified command string
var command = "run_system"
val f = Flags(Some("asdfasdf"), None, None, Some(true), Some(false))
command = f.applyToCommand(command)
I would recommend a complete redesign of your current approach.
Every member of your Flags class should be it's own case class, extending a common Flag class.
So you can define functions to combine different flags to one configuration. This configuration can than, in a final step, be used to build your result string.
abstract class Flag(name: String, parameter : Option[String])
case class Cache(parameter : Option[String]) extends Flag("-cache", parameter)
case class ErrorLog(parameter : Option[String]) extends Flag("-errorlog", parameter)
//...
type Config = List[Flag]
def applyToCommand(commandName : String, config : Config) = {
def buildString(f:Flag) =
s" $f.name${f.parameter.map(" " ++ _).getOrElse("")}"
val flagsString = config.map(buildString).mkString("")
s"$commandName" ++ flagString
}
//Now you can it simply use it as I described above
val config = List(Cache(Some("asdf")), ErrorLog(None))
applyToCommand("run_system", config)
This makes your code more flexible and easier to refactor.
At last here are some advises how you could modify this design to better fit your needs:
If you need to group your flags, you can put them in objects or separate files. Or if you want to change their behavior based on the group you can enhance the class hierarchy and add an intermediate layer.
You can move the parameter from Flag down to the case classes, so every Flag can define if it needs parameters, if yes how many and if those are optional or not.
You could also implement buildString at the case classes so every flag can decide how to format it self.
If you want do add new Flags you simply add a new class and that's it, no need to add anything to an unrelated class.
As explained #bmaderbacher, I think you should separate the different flags in the different case class.
But to answer your question, you should modify applyToCommand:
def applyToCommand(implicit command: String): String = {
var s = update(cache, "-cache")(command)
s = update(errorlog, "-error")(s)
s = update(accesslog, "-access")(s)
s = update(auth, "-do-auth")(s)
s
}
At this point it should be clear that you didn't make the right choice for your Flag class.
I'll do something like that:
trait Flag {
def toString: String
}
case class Command(value: String) {
def add(flag: Flag) = Command(value + ' ' + flag.toString)
def +(flag: Flag) = add(flag)
}
case class Cache(size: Int) extends Flag {
def toString = s"--cache $size"
}
case object Auth extends Flag {
def toString = "--auth"
}
Now you can do something like:
val command = Command("run") + Cache(500) + Auth

Parsing command line args and executing a function in scala

I am trying to parse commandline arguments and execute a function that takes the parameters upon successful extraction of the parameters. I have an object called CurrencyExchangeRunner where the main method is. I have envisioned the structure of the class as follows:
object CurrencyExtractionRunner {
def main(args:Array[String]){
parseArgs(args){
(currencyType,currencyTypeArgs) =>
CurrencyExchanger(curencyType,currencyTypeArgs){
(exchanger) => exchanger.startExchange
}
}
}
}
}
What I want to accomplish above is to parse the arguments using parseArgs(args), get the (currencyType,currencyTypeArgs) as parameters and pass those into the CurrencyExchanger factory object and then that would return the appropriate exchanger on which I will execute the startExchange method. This is what I have envisioned but I am a little clueless on how would I go about creating this flow. The first thing I tried was to create a trait that parses the command-line args as follows(I am using the jcommander library for the commandline parse):
object Args {
#Parameter(
names = Array("-h", "--help"), help = true)
var help = false
#Parameter(
names = Array("-c", "--currency-type"),
description = "Type of currency exchange that needs to be performed",
required = true)
var currencyType: String = null
#Parameter(
names = Array("-d", "--denominations"),
description = "Specific denominations to be used during the exchage")
var exchangeDenomination: String = null
#Parameter(
names = Array("-s", "--someotheroptionalarg"),
description = "Additional argument for a specific currency exchange")
var someOtherOptionalArg: String = null
}
trait ParseUtils {
//How do I do this, take the args and return a function.
def parseArgs(args: Array[String]){
val jCommander = new JCommander(Args, args.toArray: _*)
if (Args.help) {
jCommander.usage()
System.exit(0)
}
//What do I do now? How do I proceed with executing the function with
//the specific arguments?
//What do I need to do to wrap the commandline arguments so that it could
//be passed to the next function
}
}
I am pretty stuck here since I am not sure how would I make the code flexible enough to take the arbitrary sequence of commandline args and execute the next step which is the factory that returns that takes these arguments and returns the correct exchanger.
It will be great if someone could point me in the right direction.
I'm not sure why you'd use such unusual syntax to pass return values to the following methods.
I would go for a simpler solution that looks like
trait ParseUtils {
//Why would you return a function here?
//Is it a strict constraint you need to fulfill?
def parseArgs(args: Array[String]): (String, String) {
val jCommander = new JCommander(Args, args.toArray: _*)
if (Args.help) {
jCommander.usage()
System.exit(0)
}
//This is the return value of the method, a pair of parameters
(Args.currencyType, Args.exchangeDenomination)
//If you need to embed additional params, you should append them to existing one
// or you could create optional values from the Args members...
// e.g. (Args.currencyType, Args.exchangeDenomination, Option(Args.someOtherOptionalArg))
// with return type (String, String, Option[String])
}
}
object CurrencyExtractionRunner with ParseUtils {
def main(args:Array[String]){
val (currencyType,currencyTypeArgs) = parseArgs(args)
CurrencyExchanger(currencyType,currencyTypeArgs).startExchange
}
}
case class CurrencyExchanger(currencyType: String, currencyTypeArgs: String) {
def startExchange = //implementation details using the costructor arguments
}
Alternative solution
since I prefer parseArgs to be more "functional" I'd change it to
trait ParseUtils {
def parseArgs(args: Array[String]): Option[(String, String)] {
val jCommander = new JCommander(Args, args.toArray: _*)
if (Args.help) {
jCommander.usage()
None
} else
Some(Args.currencyType, Args.exchangeDenomination)
}
}
object CurrencyExtractionRunner with ParseUtils {
def main(args:Array[String]){
parseArgs(args).foreach {
case (currencyType,currencyTypeArgs) =>
CurrencyExchanger(currencyType,currencyTypeArgs).startExchange
}
}
}
case class CurrencyExchanger(currencyType: String, currencyTypeArgs: String) {
def startExchange = //implementation details using the costructor arguments
}