Scala Finch post combinator: how to get the raw Request? - scala

I need to log the raw request whenever I receive a particular post "Event" using Finch's post().
I have something like that:
val myEvent: Endpoint[String] = post("somepath" :: ipRangeEndpoint :: path[Long] :: jsonBody[Event]) {
(matchId: Long, event: Event) =>
(event match {
case _: Event.ToBeLogged =>
logger.debug(<REQUESTHERE>)
Ok("logged")
})
}
so let's say that, for a subset of cases, I need to log an incoming com.twitter.finagle.http.Request. How can I do that?

https://finagle.github.io/finch/user-guide.html#root-request
"It’s possible that Finch might be missing some of handy endpoints out of the box, especially that it’s evolved separately from Finagle. To overcome this and provide an extension point, there is a special endpoint instance, called root that returns a raw Finagle Request."

Related

A journey from akka-stream to fs2 - how to define an akka-stream http flow like stage in fs2 using http4s

i'm on my journey to deepen my knowledge in fs2, and want to try fs2-kafka for a use case where i would replace akka stream. The idea is simple, read from kafka and post data via http request to a sink, then commit back to kafka on success. So far i can't really figure out the http part. In akka stream / akka http you have out of the box a flow for that https://doc.akka.io/docs/akka-http/current/client-side/host-level.html#using-a-host-connection-pool
Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool]
Which integrate flawlessly with akka stream.
I was trying to see if i could do something similar with http4s and fs2 .
Does anyone has any reference, code sample, blog and what not that shows how to do that kind of integration. So far the only thing i could think of was, wrapping the the stream into the use method of the client resource i.e
BlazeClientBuilder[IO](IORuntime.global.compute).resource.use { ..... run stream here ..... }
Even then i am not sure of the entire thing
The thing with the typelevel ecosystem is that everything is just a library, you don't need examples on how many of them interact together, you just need to understand how each library works and the basic rules of composition.
def createClient(/** whatever arguments you need */): Resource[IO, Client[IO]] = {
// Fill this based on the documentation of the client of your choice:
// I would recommend the ember client from http4s:
// https://http4s.org/v0.23/api/org/http4s/ember/client/emberclientbuilder
}
def sendHttpRequest(client: Client[IO])(data: Data): IO[Result] = {
// Fill this based on the documentation of your client:
// https://http4s.org/v0.23/client/
// https://http4s.org/v0.23/api/org/http4s/client/client
}
def getStreamOfRecords(/** whatever arguments you need */): Stream[IO, CommittableConsumerRecord[IO, Key, Data]] = {
// Fill this based on the documentation of fs2-kafka:
// https://fd4s.github.io/fs2-kafka/docs/consumers
}
def program(/** whatever arguments you need */): Stream[IO, Unit] = {
// Based on the documentation of fs2 and fs2-kafka I would guess something like this:
Stream.fromResource(createClient(...)).flatMap { client =>
getStreamOfRecords(...).evalMapFilter { committable =>
sendHttpRequest(client)(data = committable.record).map { result =>
if (result.isSuccess) Some(committable.offset)
else None
}
}.through(commitBatchWithin(...))
}
}
object Main extends IOApp.Simple {
override final val run: IO[Unit] =
program(...).compile.drain
}
Note that I wrote all this on top of my head and with just a quick glimpse of the documentation, you need to change many things (especially types, like Data & Result). As well as tunning things like error handling and when to commit back to Kafka.
However, I expect this helps you to get an idea of how to structure your code.

Create two or more APIs with same URL in play framework

I have use case where I need to read value from query string.
Currently I have two different APIs(Some other person has created the code) which maps to same URL
GET /service/class/:className/details controllers.Student.getStudentDetails(studentId)
GET /service/class/:className/details controllers.Student.getAllStudentsDetails()
If query string is present in URL then API1 should execute, otherwise API2.
As URL is same for both APIs, I am able to hit only get-student-details API(Because it has higher priority in routes file).
I am looking for alternatives to fix this problem.
As per my knowledge we don't need to create different APIs just to handle query strings.
I am thinking to merge 2 different APIs in single APIs which takes action depending upon presence of query string in request.
What I want to know is if there is way to execute two different APIs which maps to same URL(Only difference is with query string).
NOTE: I am using play 2.4.6.
I see few ways using a single controller function (say we chose getStudentDetails)
1) Having an Option parameter:
def getStudentDetails(studentId: Option[String]) = Action { studentId match {
case Some(id) => // do something
case None => // do something else
}
// ..
}
2) Look for your query string parameters inside your http request:
def getStudentDetails = Action { request =>
request.queryString.get("studentId") match {
case Some(list) => // do something...beware this is a List
case None => // do something else
}
//...
}

Play 2.3 - Changing to WebSocket.tryAccept from async

I'm new rather new to Scala so I think this might be a very small problem.
I'm currently trying to change the method chat from using the deprecated WebSocket.async to WebSocket.tryAccept. The application uses the sample chat found at PlayFramework websocket-chat
I'm having trouble creating the complex Future type that the method requires.
This is the old method:
def chat() = WebSocket.async[JsValue] {
request =>
ChatRoom.join("User: 1")
}
New method:
def chat2() = WebSocket.tryAccept[JsValue] {
request =>
try {
// ChatRoom.join returns (iteratee,enumerator)
ChatRoom.join("User: 1").map(e => Right(e))
} catch {
case e: Exception =>
Left(Ok("Failed")) // Error here
}
}
My error message:
found : Left[Result,Nothing]
required: Future[Either[Result,(Iteratee[JsValue, _], Enumerator[JsValue])]]
I have no idea how I am supposed to create such a complex result for such a simple message.
Although ChatRoom.join("User: 1").map(e => Right(e)) doesn't show any errors now, I'm unsure if this is the correct implementation.
I'm not in front of an IDE at the moment, so I can't answer fully, but the return type it's asking for isn't as complex as it seems. An "Either" is a "Left" or a "Right" in the same way that an "Option" is a "Some" or a "None". So what it's asking for is a Future (which Websocket.async should also have required) that contains either a Left[Result] -- the fail-to-connect case, or a Right[(Iteratee, Enumerator)] -- the success case. Assuming that Chatroom.join returns a Future[(Iteratee, Enumerator)], the map operation is simply wrapping that in a "Right". The first thing I'd try is wrapping Left(Ok("Failed")) in a Future and see what happens.

How to filter requests for access code in Play 2.1

I'd like to add a (per method / global) filter to requests, that simply rejects (404/403 page) any request that doesn't have a specific URL parameter.
I know Play has one-two mechanism to do this (e.g. register on Global.onRouteRequest()), so don't just send me a link to the documentation unless it contains a code sample that covers this question. I tried playing with the API but got a bit stuck.
Is this what you mean?
object Global extends WithFilters(AccessCheck)
object AccessCheck extends Filter with Results {
override def apply(next:RequestHeader => Result)(request:RequestHeader):Result =
request
.getQueryString("myCheck")
.map( myCheck => next(request))
.getOrElse(Forbidden)
}
http://www.playframework.com/documentation/2.1.0/ScalaInterceptors
If you are just trying to make some reusable code to filter requests on specific actions you my want to try creating an EssentialAction like this. This is known as action composition. This is what it would look like in the case you described. There is more information in the docs: http://www.playframework.com/documentation/2.1.1/ScalaActionsComposition
Note that you can do this in Play 2.0.X as well but EssentialAction doesn't exist, instead you use an Action, and there is just a little more syntax involved.
def RequireMyCheck(action: => EssentialAction): EssentialAction = {
EssentialAction { request =>
request
.getQueryString("myCheck")
.map( myCheck => action()(request))
.getOrElse(Forbidden)
}
}
You can use it like this:
def index = RequireMyCheck {
Action { request =>
Ok("Hello")
}
}

Request Content-Type in Play! Framework for REST webservices

I'm trying to implement a REST webservice with the Play! framework. I know how I can return a response in different formats (JSON, XML, HTML, ...) by specifying multiple templates. However, I didn't find any information on how you should process different Content-Types in a (e.g. POST) request (form encoded, JSON, XML, ...).
Is it possible to annotate a method to match only certain Content-Types (something like #Consumes)? Do I have to differentiate between the different request Content-Types with an if-clause in the controller method?
Have a look at the Play documentation for combining body parsers:
http://www.playframework.com/documentation/2.2.0/ScalaBodyParsers
If you want to constrain a post body to only xml or json you can write something along these lines:
val xmlOrJson = parse.using {
request =>
request.contentType.map(_.toLowerCase(Locale.ENGLISH)) match {
case Some("application/json") | Some("text/json") => play.api.mvc.BodyParsers.parse.json
case Some("application/xml") | Some("text/xml") => play.api.mvc.BodyParsers.parse.xml
case _ => play.api.mvc.BodyParsers.parse.error(Future.successful(UnsupportedMediaType("Invalid content type specified")))
}
}
def test = Action(xmlOrJson) {
request =>
request.body match {
case json: JsObject => Ok(json) //echo back posted json
case xml: NodeSeq => Ok(xml) //echo back posted XML
}
}
The xmlOrJson function looks at the content type request header to determine the body parser. If it is not xml or json then it returns the error body parser with an UnsupportedMediaType response (HTTP 415).
You then pass this function in as the body parser of any action you want to constrain to xml or json content. Within the action you can look at the body to determine if xml or json was parsed and process accordingly.
You don't do it through annotation, but rather through your routes file, or through an if statement in your action. Depends on your use case as to which one is best suited.
The following URL gives you some information on the routes file for content negotiation. http://www.playframework.org/documentation/1.2.4/routes#content-types
Example
GET /index.xml Application.index(format:'xml')
GET /index.json Application.indexJson(format:'json')
The above calls different actions, but you could call the same action with a different format value if you wish.
You may use default parser together with pattern matching in Play 2 with Scala. Something like this:
def myAction() = Action { req =>
req.body match {
case AnyContentAsFormUrlEncoded(m) =>
val f1 = m.get("field1").flatMap(_.headOption)
...
case AnyContentAsJson(j) =>
val f1 = (j \ "field1").asOpt[String]
...
case _ => //handle content types you don't support
...
}