Difference between query string and path parameter in play framework? - scala

I'm learning play framework by my self.
What is the difference between a query string and a path parameter in routers? and what is the difference between PathBindable and QueryStringBindable ?

It's described in the documentation:
https://www.playframework.com/documentation/2.5.x/ScalaRouting
https://www.playframework.com/documentation/2.5.x/ScalaRequestBinders
In a short, path parameter is a parameter in the URL:
# Client, like /clients/125
GET /clients/:id controllers.Clients.show(id: Long)
Query string parameters are query string parameters:
# Pagination links, like /clients?page=3
GET /clients controllers.Clients.list(page: Int ?= 1)
You can see that in this example Play binds parameters from string to other types, like Long and Int. There are situations when you want to bind parameters to other types, then you need to write your own binder. PathBindable for path parameter and QueryStringBindable for query parameters.
So, if you can bind client id to client you can write
# Client, like /clients/125
GET /clients/:id controllers.Clients.show(client: Client)
And the binder:
implicit def pathBinder(implicit intBinder: PathBindable[Int]) = new PathBindable[Client] {
override def bind(key: String, value: String): Either[String, Client] = {
for {
id <- intBinder.bind(key, value).right
client <- Client.findById(id).toRight("Client not found").right
} yield client
}
override def unbind(key: String, client: Client): String = {
client.id.toString
}
}
If you want to have full control over URL parsing and binding, you can go even further and use String Interpolating Routing DSL

Related

Scala - how to get data from IO[HttpResponse] in Hammock?

I have a simple methods:
def retrieveRepositories(url: String, params: String): IO[HttpResponse] = Hammock.getWithOpts(uri"$url", createOpts).exec[IO]
Which is a http client. and json decoder:
implicit def decodeResponseEntity(response: HttpResponse): Either[CodecException, List[GitRepository]] = Decoder[List[GitRepository]].decode(response.entity)
Now I want to call this client like this:
def getRepos(organization: String, params: String): F[Either[CodecException, List[GitRepository]]] = for {
res <- retrieveRepositories(organization, params)
result <- Sync[F].delay(decodeResponseEntity(res))
} yield result
But, there is a problem with result <- Sync[F].delay(decodeResponseEntity(res)) line, because I got an error: Type mismatch. Reguired: IO[B_] but found F[Either[CodecException, List[GitRepository]]]. When I add unsafeRunSync() method to retrieveRepositories(organization, params) then it works ok, but I should call this method in the end and not here. How should I fix it?
If you can, you may want to change the definition of retrieveRepositories and parameterize on the effect type (F) instead of using the concrete IO type.
If you can't change retrieveRepositories, add a implicit LiftIO constraint in getRepos. You will be able to use liftIO method to lift concrete IO values into F. An alternative would be use the Async typeclass, which inherits from both Sync and LiftIO.
See documentation for liftIO: https://typelevel.org/cats-effect/typeclasses/liftio.html

Spray - deserializing optional query parameter

From spray.io documentation page:
color
extract value of parameter “color” as String
color.?
extract optional value of parameter “color” as Option[String]
amount.as[Int]
extract value of parameter “amount” as Int, you need a matching Deserializer in scope for that to work (see also Unmarshalling)
So how can I parse optional Int value? Something like amount.?.as[Int] doesn't seem to work.
You can see the details here: https://github.com/spray/spray/blob/76ab89c25ce6d4ff2c4b286efcc92ee02ced6eff/spray-routing/src/main/scala/spray/routing/directives/NameReceptacle.scala
case class NameReceptacle[A](name: String) {
def as[B] = NameReceptacle[B](name)
def as[B](deserializer: FSOD[B]) = NameDeserializerReceptacle(name, deserializer)
def ? = as[Option[A]]
def ?[B](default: B) = NameDefaultReceptacle(name, default)
def ![B](requiredValue: B) = RequiredValueReceptacle(name, requiredValue)
}
The straightforward syntax would be
"amount".as[Option[Int]]
Unfortunately there is no syntactic sugar to create a NameRecaptable for an option type directly, but you can do it in two steps:
"amount".as[Int].as[Option[Int]]
? is an alias for NameRecaptable[A].as[Option[A]], so you can use the following code (note the postfix operator syntax):
"amount".as[Int]?

Query parameter as List

I want to have a way access certain query parameters for all of my requests. An example query would be something like:
http://api.mysite.com/accounts/123?include=friends,photos
Where I want to get access to the comma separated list of include relationships.
As far as I could tell, the following doesn't work and will look at the include list as a single string:
// routes.txt
GET /accounts/:id controllers.AccountsController.get(id: Int, include: Seq[String])
This is how I am currently doing it, but I was hoping there was a cleaner way.
// routes.txt
GET /accounts/:id controllers.AccountsController.get(id: Int, include: Option[String])
// AccountsController.scala
def get(id: Int, include: Option[String]) = Action {
// Convert the option to a set
val set = if (include.isDefined) include.get.split(",").toSet else Set()
}
The proper way to do it (already supported by Play) would be to use repeated key-values in the query string, i.e.:
http://api.mysite.com/accounts/123?include=friends&include=photos
That would automatically bind Seq("friends", "photos") to include in that route. This has the advantage of being able to use commas within keys, and is consistent with the common usage of query string parameters.
Alternatively, you can create a custom QueryStringBindable[List[String]] that can handle a comma-separated list. Something like:
object QueryStringBinders {
implicit def listBinder(key: String)(implicit stringBinder: QueryStringBindable[String]) = {
new QueryStringBindable[List[String]] {
override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, List[String]]] =
stringBinder.bind(key, params).map(_.right.map(_.split(",").toList))
override def unbind(key: String, strings: List[String]): String =
s"""$key=${strings.mkString(",")}"""
}
}
}
Then you would use PlayKeys.routesImport += "QueryStringBinders._" within build.sbt to use it (or whatever the fully qualified package name is). Using a QueryStringBindable would make the split logic reusable, with minimal boilerplate.
As, #m-z said the proper way to use is repeated key-values in query string like: http://api.mysite.com/accounts/123?include=friends&include=photos
and in you action you can access query string with queryString method as well i.e
your route will look like this:
GET /accounts/:id controllers.AccountsController.get(id: Int)
and in your controller:
// AccountsController.scala
def get(id: Int) = Action { request =>
// the `request.queryString` will give you Map[String, Seq[String]] i.e all they keys and their values
val includes = request.queryString.getOrElse("include", Nil)
}

Convert from Map (Play framework request.body.asFormUrlEncoded) to case class, and vice versa, with less boilerplate (interacting with untyped world)

Here is a concrete example, although I have been encountering this problem in the more general case.
I am working on an integration between a Play 2.3 web app and Slack through a custom slash command. (Note: Slack has docs on the custom slash command API format, but I don't believe I can deep link them directly for a logged-out reader.)
Slack will (when invoked appropriately by a user) POST to my endpoint with standard form URL encoded body with a known format. Their official example is:
token=KpADMkoKxZJRGKTG8kJoWXGC
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/weather
text=94070
In my app, I am modeling this request with a case class.
case class SlashCommand(
token: String,
teamId: String,
teamDomain: String,
channelId: String,
channelName: String,
userId: String,
userName: String,
command: String,
text: String
)
By the way, is there some way to better enforce type constraints other than making each field a String?
I am having trouble converting from the Play framework representation of form URL encoded to the case class. Here is the code I have currently, which I believe works correctly, but which I feel is overly repetitive.
object SlashCommand {
def parseFromMap(in: Map[String, String]): Option[SlashCommand] = {
for {
token <- in.get("token")
teamId <- in.get("team_id")
teamDomain <- in.get("team_domain")
channelId <- in.get("channel_id")
channelName <- in.get("channel_name")
userId <- in.get("user_id")
userName <- in.get("user_name")
command <- in.get("command")
text <- in.get("text")
} yield SlashCommand(
token,
teamId,
teamDomain,
channelId,
channelName,
userId,
userName,
command,
text
)
}
def parseFromRequest(req: Request[AnyContent]): Option[SlashCommand] = {
req.body.asFormUrlEncoded.map { m =>
m.mapValues(_.last)
}.flatMap(parseFromMap)
}
}
The somewhat repetitive parts that I would ideally like to factor out are:
explicit for with explicit gets
explicit construction of case class
manual conversion of Request[AnyContent] to Map[String, Option[String] to Map[String, String]
Are there any methods to reduce repetition and improve readability in these conversions?
Additionally, I am likely to need the reverse soon enough, i.e. a conversion from case class to Map and/or form URL encoded and/or JSON. Any methods to reduce repetition there?
Thanks in advance!
EDIT: I've been thinking about this further, and I think there may be some things I can do using Play framework form handling for the forward case.
The play Forms API handles binding from Request, Map[String, String] or JsValue to a type of your liking (using Form.bind and Form.bindFromRequest) and has got a declarative-ish way of describing constraints. So your thinking-edit sounds right to me!
More about the forms API in the Play docs:
https://www.playframework.com/documentation/2.3.x/ScalaForms

How can I combine fluent interfaces with a functional style in Scala?

I've been reading about the OO 'fluent interface' approach in Java, JavaScript and Scala and I like the look of it, but have been struggling to see how to reconcile it with a more type-based/functional approach in Scala.
To give a very specific example of what I mean: I've written an API client which can be invoked like this:
val response = MyTargetApi.get("orders", 24)
The return value from get() is a Tuple3 type called RestfulResponse, as defined in my package object:
// 1. Return code
// 2. Response headers
// 2. Response body (Option)
type RestfulResponse = (Int, List[String], Option[String])
This works fine - and I don't really want to sacrifice the functional simplicity of a tuple return value - but I would like to extend the library with various 'fluent' method calls, perhaps something like this:
val response = MyTargetApi.get("customers", 55).throwIfError()
// Or perhaps:
MyTargetApi.get("orders", 24).debugPrint(verbose=true)
How can I combine the functional simplicity of get() returning a typed tuple (or similar) with the ability to add more 'fluent' capabilities to my API?
It seems you are dealing with a client side API of a rest style communication. Your get method seems to be what triggers the actual request/response cycle. It looks like you'd have to deal with this:
properties of the transport (like credentials, debug level, error handling)
providing data for the input (your id and type of record (order or customer)
doing something with the results
I think for the properties of the transport, you can put some of it into the constructor of the MyTargetApi object, but you can also create a query object that will store those for a single query and can be set in a fluent way using a query() method:
MyTargetApi.query().debugPrint(verbose=true).throwIfError()
This would return some stateful Query object that stores the value for log level, error handling. For providing the data for the input, you can also use the query object to set those values but instead of returning your response return a QueryResult:
class Query {
def debugPrint(verbose: Boolean): this.type = { _verbose = verbose; this }
def throwIfError(): this.type = { ... }
def get(tpe: String, id: Int): QueryResult[RestfulResponse] =
new QueryResult[RestfulResponse] {
def run(): RestfulResponse = // code to make rest call goes here
}
}
trait QueryResult[A] { self =>
def map[B](f: (A) => B): QueryResult[B] = new QueryResult[B] {
def run(): B = f(self.run())
}
def flatMap[B](f: (A) => QueryResult[B]) = new QueryResult[B] {
def run(): B = f(self.run()).run()
}
def run(): A
}
Then to eventually get the results you call run. So at the end of the day you can call it like this:
MyTargetApi.query()
.debugPrint(verbose=true)
.throwIfError()
.get("customers", 22)
.map(resp => resp._3.map(_.length)) // body
.run()
Which should be a verbose request that will error out on issue, retrieve the customers with id 22, keep the body and get its length as an Option[Int].
The idea is that you can use map to define computations on a result you do not yet have. If we add flatMap to it, then you could also combine two computations from two different queries.
To be honest, I think it sounds like you need to feel your way around a little more because the example is not obviously functional, nor particularly fluent. It seems you might be mixing up fluency with not-idempotent in the sense that your debugPrint method is presumably performing I/O and the throwIfError is throwing exceptions. Is that what you mean?
If you are referring to whether a stateful builder is functional, the answer is "not in the purest sense". However, note that a builder does not have to be stateful.
case class Person(name: String, age: Int)
Firstly; this can be created using named parameters:
Person(name="Oxbow", age=36)
Or, a stateless builder:
object Person {
def withName(name: String)
= new { def andAge(age: Int) = new Person(name, age) }
}
Hey presto:
scala> Person withName "Oxbow" andAge 36
As to your use of untyped strings to define the query you are making; this is poor form in a statically-typed language. What is more, there is no need:
sealed trait Query
case object orders extends Query
def get(query: Query): Result
Hey presto:
api get orders
Although, I think this is a bad idea - you shouldn't have a single method which can give you back notionally completely different types of results
To conclude: I personally think there is no reason whatsoever that fluency and functional cannot mix, since functional just indicates the lack of mutable state and the strong preference for idempotent functions to perform your logic in.
Here's one for you:
args.map(_.toInt)
args map toInt
I would argue that the second is more fluent. It's possible if you define:
val toInt = (_ : String).toInt
That is; if you define a function. I find functions and fluency mix very well in Scala.
You could try having get() return a wrapper object that might look something like this
type RestfulResponse = (Int, List[String], Option[String])
class ResponseWrapper(private rr: RestfulResponse /* and maybe some flags as additional arguments, or something? */) {
def get : RestfulResponse = rr
def throwIfError : RestfulResponse = {
// Throw your exception if you detect an error
rr // And return the response if you didn't detect an error
}
def debugPrint(verbose: Boolean, /* whatever other parameters you had in mind */) {
// All of your debugging printing logic
}
// Any and all other methods that you want this API response to be able to execute
}
Basically, this allows you to put your response into a contain that has all of these nice methods that you want, and, if you simply want to get the wrapped response, you can just call the wrapper's get() method.
Of course, the downside of this is that you will need to change your API a bit, if that's worrisome to you at all. Well... you could probably avoid needing to change your API, actually, if you, instead, created an implicit conversion from RestfulResponse to ResponseWrapper and vice versa. That's something worth considering.