Play Framework : Lining up a FakeRequest with an temporary file action wrapped in maxLength - scala

Trying to test a play controller, but caught in their super clever meta-programming tar-pit.
How do I line up a FakeRequest with a temporary file parsing action wrapped with a maxLength? :
trait FileActionUtils { self : Controller =>
implicit class ByteSizeHelper(base : Int) {
def KB = base * 1024
def MB = base * 1024 * 1024
}
def singleSizeCappedFileAction(size: Int, filename: String)(fileHandler : (File) => Result) = {
Action(maxLength(size, parser = parse.multipartFormData)) {
_.body match {
case Right(body) =>
body.file(filename) match {
case Some(file) =>
fileHandler(file.ref.file)
case None =>
BadRequest.withHeaders("message" -> "\"file\" not encoded in multipart body.")
}
case Left(error) =>
BadRequest.withHeaders("message" -> "file is too large.")
}
}
}
}
What I am trying :
....
val body = new MultipartFormData(Map(),
List(
FilePart("file", "file1.mpp", Some("Content-Type: multipart/form-data"), TemporaryFile("test/fixtures/file1.mpp"))
),
Nil,Nil
)
val request = FakeRequest().withMultipartFormDataBody(body)
val result = controller.parseProjectFile("").apply(request)
println(contentAsString(result))
....
I'm expecting a string or something I can work with on the other side as the test shows. Plus, I know for a fact the controller is working fine.

Related

Sending zio http response from callback function

I am trying to play around with ZIO http using their simples hello world example. I have a Java-written service which does some logic, and it expecting a handler function, so it can call it when result is ready. How do I user it together with ZIO http ?
I want something like this:
object HelloWorld extends App {
def app(service: JavaService) = Http.collect[Request] {
case Method.GET -> Root / "text" => {
service.doSomeStuffWIthCallback((s:String) => Response.text(s))
}
}
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] =
Server.start(8090, app(new JavaService)).exitCode
}
Basically I want to send ZIO HTTP response from the callback function, I am just not sure how to go about that. Thanks.
EDIT:
I coudn't get the right types from your code, so I decided to simplify the whole thing, and arrived to this:
val content: HttpData[Blocking, Throwable] = HttpData.fromStream {
ZStream.fromEffect(doSomeStuffWrapped)
}
def doSomeStuffWrapped = {
UIO.effectAsync[String] { cb =>
cb(
IO.succeed("TEST STRING")
)
}
}
However, the issue here is that types do not match, HttpData.fromStream requires ZStream of byte
Here is the link to my gist:
https://gist.github.com/pmkyl/a37ff8b49e013c4e2e6f8ab5ad83e258
You should wrap your Java service with callback in an effect using effectAsync:
def doSomeStuffWrapped(service: JavaService): Task[String] = {
IO.effectAsync[Throwable, String] { cb =>
service.doSomeStuffWithCallback((s: String) => {
// Success case
cb(IO.succeed(s))
// Optional error case?
// cb(IO.fail(someException))
})
}
}
def app(service: JavaService) = Http.collectM[Request] {
case Method.GET -> Root / "text" => {
doSomeStuffWrapped(service)
.fold(err => {
// Handle errors in some way
Response.text("An error occured")
}, successStr => {
Response.text(successStr)
})
}
}
You might want to see this article presenting different options for wrapping impure code in ZIO: https://medium.com/#ghostdogpr/wrapping-impure-code-with-zio-9265c219e2e
In ZIO-http v1.0.0.0-RC18 HttpData.fromStream can also take ZStream[R, E, String] as input with Http charset which defaults to CharsetUtil.UTF_8 however you can pass any charset to the HttpData.fromStream as its second argument. you can find the solution below
val stream: ZStream[Any, Nothing, String] = ZStream.fromEffect(doSomeStuffWrapped)
val content: HttpData[Any, Nothing] = HttpData.fromStream(stream)
def doSomeStuffWrapped = {
UIO.effectAsync[String] { cb =>
cb(
IO.succeed("TEST STRING"),
)
}
}
// Create HTTP route
val app = Http.collect[Request] {
case Method.GET -> !! / "health" => Response.ok
case Method.GET -> !! / "file" => Response(data = content)
}
// Run it like any simple app
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] =
Server.start(8090, app.silent).exitCode
However in previous versions you could have done something like given below to make it work
val stream: ZStream[Any, Nothing, Byte] =
ZStream.fromEffect(doSomeStuffWrapped).mapChunks(_.map(x => Chunk.fromArray(x.getBytes(HTTP_CHARSET))).flatten)
val content: HttpData[Any, Nothing] = HttpData.fromStream(stream)
def doSomeStuffWrapped = {
UIO.effectAsync[String] { cb =>
cb(
IO.succeed("TEST STRING"),
)
}
}
// Create HTTP route
val app = Http.collect[Request] {
case Method.GET -> !! / "health" => Response.ok
case Method.GET -> !! / "file" => Response(data = content)
}
// Run it like any simple app
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] =
Server.start(8090, app.silent).exitCode
Here is also another way of achieving the same result:
case class MyService(name: String) {
def imDone[R, E](s: String => Unit): Unit = s(name)
}
val s: MyService = MyService("test")
val app: Http[Any, Nothing, Request, UResponse] = Http.collectM[Request] { case Method.GET -> Root / "text" =>
ZIO.effectAsync[Any, Nothing, UResponse] { cb =>
s.imDone { b =>
cb(IO.succeed(Response.text(b)))
}
}
}

How do I compose nested case classes populated with async mongodb queries in play framework

I have been trying to convert mongodb queries I have working with await to using totally async. The problem is, I cannot find any examples or get code working to populate a list of objects where for each object there is a nested find returning futures.
I have seen examples for a single object, such as
val user = mongoDao.getUser(id)
val address = mongoDao.getAddress(user.id)
for that I see for comprehension works just fine. However, I have a list of objects (similar to users) and I cant seem to get the code right.
What I need to do is get all the users in an async manner, then when they complete, get all the addresses and populate a field (or create a new case class.)
val usersFuture : Future[List[User]] = mongoDao.getUsers()
val fullFutures : Future[List[FullUser]] = usersFuture.map(users: List[User] => {
users.map(user: User => {
val futureAddress : Future[Address] = mongoDao.getAddress()
// Now create a object
futureAddress.map(address: Address) {
FullUserInfo(user, address)
}
}
}
So, I'd like to end up with a Future[List[FullUser]] that I can return to the play framework. I've included the cutdown I've tried.
thanks
// OBJECTS HERE
case class Outer(id: Int, name: String)
case class Inner(id: Int, name: String)
case class Combined(id: Int, name: String, inner: Inner)
// FAKE DAO to reproduct
#Singleton
class StatInner #Inject()( implicit val ec: ExecutionContext) {
def outer() = {
Future {
val lb = new ListBuffer[Outer]()
Thread.sleep(1000)
println("Done")
for (id <- 1 to 5) {
lb += Outer(id, s"Hello $id")
}
lb.toList
}
}
def inner(id: Int) : Future[Inner] = {
Future {
Thread.sleep(1000)
Inner(id, s"inner $id")
}
}
}
// CODE to query that is not working
def nestedTree = Action.async {
val statInner : StatInner = new StatInner()
val listouter : Future[List[Outer]] = statInner.outer()
val combined = listouter.map((listOuter : List[Outer]) => {
listOuter.flatMap((outer: Outer) => {
val futInner : Future[Inner] = statInner.inner(outer.id)
futInner.map((inner: Inner) => {
Combined(outer, inner)
})
})
})
combined.map(Json.toJson(_))
}
```
Use Future.{flatMap.sequence}:
val usersFuture: Future[List[User]] = mongoDao.getUsers()
val fullFutures: Future[List[FullUser]] = usersFuture.flatMap { users =>
Future.sequence(users.map { user =>
mongoDao.getAddress().map { adress =>
FullUserInfo(user, address)
}
})
}

How to use `flatMap` and `map` to fill a `list` on Play framework + Scala?

I am struggling to use flatMap and map with Play framework + Scala. This method has a bunch of other issues, but I am trying to go through them one for each time. The first thing that I cannot figure out how to implement is how to fill a Seq inside nested flatMap and map and return a Json output. Here is my method:
def getRacks(at: String) = Action.async { implicit request: Request[AnyContent] =>
var rackSeq: Seq[Rack] = Seq.empty
var gpuSeq: Seq[Gpu] = Seq.empty
rackRepository.get(Util.toTime(at)).flatMap { resultRack: Seq[RackRow] =>
resultRack.map { r: RackRow =>
gpuRepository.getByRack(r.id).map { result: Seq[GpuRow] =>
result.map { gpuRow: GpuRow =>
gpuSeq = gpuSeq :+ Gpu(gpuRow.id, gpuRow.rackId, gpuRow.produced, Util.toDate(gpuRow.installedAt))
println(gpuRow)
}
}
val rack = Rack(r.id, r.produced, Util.toDate(r.currentHour), gpuSeq)
rackSeq = rackSeq :+ rack
}
println("rackSeq: " + rackSeq)
Future.successful(Ok(Json.toJson(rackSeq)).as(JSON))
}.recover {
case pe: ParseException => BadRequest(Json.toJson("Error on parse String to time."))
case e: Exception => BadRequest(Json.toJson("Error to get racks."))
case _ => BadRequest(Json.toJson("Unknow error to get racks."))
}
}
I was expecting that rackSeq will be filled with GpuRow. but my output is like this:
rackSeq: List(Rack(rack-1,0.2,2018-01-23T14:15:00.79Z,List()))
GpuRow(rack-1-gpu-0,rack-1,0.2,1515867048515)
How to evaluate both lists to the output?
Instead of mutating variables, stay within the context of a Future and perform transformations until you reach the desired result. Assuming the following types...
rackRepository.get(Util.toTime(at)) // Future[Seq[RackRow]]
gpuRepository.getByRack(r.id) // Future[Seq[GpuRow]]
...you could do this instead:
def gpuRowToGpu(gpuRow: GpuRow): Gpu = {
Gpu(gpuRow.id, gpuRow.rackId, gpuRow.produced, Util.toDate(gpuRow.installedAt))
}
def getRacks(at: String) = Action.async { implicit request: Request[AnyContent] =>
rackRepository.get(Util.toTime(at)).flatMap { resultRack: Seq[RackRow] =>
val seqFutRack: Seq[Future[Rack]] = resultRack.map { r: RackRow =>
gpuRepository.getByRack(r.id).map { result: Seq[GpuRow] =>
val gpus = result.map(gpuRowToGpu) // Seq[Gpu]
Rack(r.id, r.produced, Util.toDate(r.currentHour), gpus)
} // Future[Rack]
}
val futSeqRack: Future[Seq[Rack]] = Future.sequence(seqFutRack)
futSeqRack.map(racks => Ok(Json.toJson(racks)).as(JSON))
}.recover {
...
}
}

Cake pattern for deep function call chain ( threading dependency along as extra parameter)

Question on simple example:
Assume we have :
1) 3 functions:
f(d:Dependency), g(d:Dependency), h(d:Dependency)
and
2) the following function call graph : f calls g, g calls h
QUESTION: In h I would like to use the d passed to f, is there a way to use the cake pattern to get access to d from h? If yes, how ?
Question on real-world example:
In the code below I manually need to thread the Handler parameter from
// TOP LEVEL , INJECTION POINT
to
//USAGE OF Handler
Is it possible to use the cake pattern instead of the manual threading? If yes, how ?
package Demo
import interop.wrappers.EditableText.EditableText
import interop.wrappers.react_sortable_hoc.SortableContainer.Props
import japgolly.scalajs.react.ReactComponentC.ReqProps
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.prefix_<^._
import interop.wrappers.react_sortable_hoc.{SortableContainer, SortableElement}
object EditableSortableListDemo {
trait Action
type Handler=Action=>Unit
object CompWithState {
case class UpdateElement(s:String) extends Action
class Backend($: BackendScope[Unit, String],r:Handler) {
def handler(s: String): Unit =
{
println("handler:"+s)
$.setState(s)
//USAGE OF Handler <<<<<=======================================
}
def render(s:String) = {
println("state:"+s)
<.div(<.span(s),EditableText(s, handler _)())
}
}
val Component = (f:Handler)=>(s:String)=>
ReactComponentB[Unit]("EditableText with state").initialState(s).backend(new Backend(_,f))
.renderBackend.build
}
// Equivalent of ({value}) => <li>{value}</li> in original demo
val itemView: Handler=>ReqProps[String, Unit, Unit, TopNode] = (f:Handler)=> ReactComponentB[String]("liView")
.render(d => {
<.div(
<.span(s"uhh ${d.props}"),
CompWithState.Component(f)("vazzeg:"+d.props)()
)
})
.build
// As in original demo
val sortableItem = (f:Handler)=>SortableElement.wrap(itemView(f))
val listView = (f:Handler)=>ReactComponentB[List[String]]("listView")
.render(d => {
<.div(
d.props.zipWithIndex.map {
case (value, index) =>
sortableItem(f)(SortableElement.Props(index = index))(value)
}
)
})
.build
// As in original demo
val sortableList: (Handler) => (Props) => (List[String]) => ReactComponentU_ =
(f:Handler)=>SortableContainer.wrap(listView(f))
// As in original SortableComponent
class Backend(scope: BackendScope[Unit, List[String]]) {
def render(props: Unit, items: List[String]) = {
def handler = ???
// TOP LEVEL , INJECTION POINT<<<<<<========================================
sortableList(handler)(
SortableContainer.Props(
onSortEnd = p => scope.modState( l => p.updatedList(l) ),
useDragHandle = false,
helperClass = "react-sortable-handler"
)
)(items)
}
}
val defaultItems = Range(0, 10).map("Item " + _).toList
val c = ReactComponentB[Unit]("SortableContainerDemo")
.initialState(defaultItems)
.backend(new Backend(_))
.render(s => s.backend.render(s.props, s.state))
.build
}

How to get array from a json formatted form in Scala?

I am new to SCALA where I am going to develop an API with PLAY and SLICK.
I am going fetch an array (json formatted all the values are in integer) from a form via a web request like as follows:-
Request data:
{"ids": [1,2,3,4,5,6,7,8,9]}
Now I would like to fetch the form array data. Therefore I have declare a Form in my controller as:-
case class IdsForm(ids: Array[Int])
private val idsForm: Form[IdsForm] = Form(
mapping(
"ids" -> ????
)(IdsForm.apply)(IdsForm.unapply)
)
Now Would like to print all the elements in that array. Hence declare a function as follows:-
def getIds() = Action.async(parse.json) {
implicit request => idsForm.bind(request.body).fold(
formWithErrors => Future.successful(BadRequest(formWithErrors.toString)),
form => {
val ids = form.ids
// Print all the array elements
for ( x <- ids ) {
println( x )
}
val responseBody = Json.obj(
"status" -> 200,
"message" -> Json.toJson("Successfully printed")
)
Ok(responseBody)
}
)
}
Please let me know, what to put instead of "ids" -> ???? on my code. In case of single number I have putted "id" -> number. I don't know what to put instead of ???? in "ids" -> ????
You shouldn't use a form to submit JSON... Here you have example how to do it. Although, you could upload a JSON file if you want?
For example(I didn't test this), your data class:
case class MyData(ids: List[Int])
object MyData { implicit val myDataJsonFormat: Json.format[MyData] }
Your controller route:
def uploadJson = Action(parse.json) { request =>
val dataResult = request.body.validate[MyData]
dataResult.fold(
errors => { BadRequest(........) },
data => {
// do whatever with data....
Ok(....)
}
)
}
I use my code as:-
case class IdsForm(ids: Seq[Int])
private val idsForm: Form[IdsForm] = Form(
mapping(
"ids" -> seq[number]
)(IdsForm.apply)(IdsForm.unapply)
)
And the function is:-
def getIds() = Action.async(parse.json) {
implicit request => idsForm.bind(request.body).fold(
formWithErrors => Future.successful(BadRequest(formWithErrors.toString)),
form => {
val ids = form.ids
// Print all the array elements
for ( x <- 0 to ids.length - 1 ) {
println( ids(x) )
}
val responseBody = Json.obj(
"status" -> 200,
"message" -> Json.toJson("Successfully printed")
)
Ok(responseBody)
}
)
}
It works fine. Is it ok?