Play Framework - Respond with JSON after uploading a file (multipartFormData) - scala

I am using this code to upload an image on server , that i get from this link play upload
def upload = Action(parse.multipartFormData) { request =>
request.body
.file("picture")
.map { picture =>
val dataParts = request.body.dataParts;
val filename = Paths.get(picture.filename).getFileName
val fileSize = picture.fileSize
val contentType = picture.contentType
val picturePaths =
picture.ref.copyTo(
Paths.get(
s"/opt/docker/images/$filename"
),
replace = true
)
if (dataParts.get("firstPoint") == None) {
val pointlocation = new Point_LocationModel(
dataParts.get("step").get(0),
dataParts.get("backgroundTargetName").get(0),
dataParts.get("x").get(0),
dataParts.get("y").get(0),
dataParts.get("z").get(0),
dataParts.get("rotation").get(0),
dataParts.get("note").get(0),
dataParts.get("tag").get(0),
dataParts.get("work_session_id").get(0),
(picturePaths).toString
)
point_LocationRepository.create(pointlocation).map { data =>
Created(Json.toJson(data._2))
}
} else {
val jValuefirstPoint =
Json.parse(dataParts.get("firstPoint").get(0)).as[PointModel]
val jValuesecondPoint =
Json.parse(dataParts.get("secondPoint").get(0)).as[PointModel]
val pointlocation = new Point_LocationModel(
dataParts.get("step").get(0),
dataParts.get("backgroundTargetName").get(0),
Some(jValuefirstPoint),
Some(jValuesecondPoint),
dataParts.get("rotation").get(0),
dataParts.get("note").get(0),
dataParts.get("tag").get(0),
dataParts.get("work_session_id").get(0),
(picturePaths).toString
)
point_LocationRepository.create(pointlocation).map { data =>
logger.info(s"repoResponse: ${data}");
Created(Json.toJson(data._2))
}
}
Ok(s"picturePaths ${picturePaths}")
}
.getOrElse(Ok("Invalid Format"))
}
This code works very well, but on the response I want to get the response from the repository. How can i await for the response of the repository to return this?
Can you give me any idea how can i do it?
Thanks in advance.

If we simplify your code to the essential bits, you have:
def upload = Action(parse.multipartFormData) { request =>
request.body
.file("picture")
.map { picture =>
if (someConditionA) {
someBusinessLogicA().map { data =>
Created(Json.toJson(data._2))
}
} else {
someBusinessLogicB().map { data =>
Created(Json.toJson(data._2))
}
}
Ok(s"picturePaths ${picturePaths}")
}
.getOrElse(Ok("Invalid Format"))
}
There are 2 problems:
the return of your if/else is swallowed by the Ok(s"picturePaths ${picturePaths}")
assuming your business logic returns Future[_] you need to "propagate" the Future everywhere
Something like this should work:
def upload = Action.async(parse.multipartFormData) { request =>
request.body
.file("picture")
.map { picture =>
if (someConditionA) {
someBusinessLogicA().map { data =>
Created(Json.toJson(data._2))
}
} else {
someBusinessLogicB().map { data =>
Created(Json.toJson(data._2))
}
}
}
.getOrElse(Future.successful(Ok("Invalid Format")))
}
Notice the Action.async and the Future in the getOrElse.

Related

Wrong PUT method gets triggered in Akka Http using scala

In my APIendpoint class, I have 2 PUT methods lets say updateA and UpdateB but when I'm trying to hit UpdateB using swagger it resolves to a UpdateA everytime. I dunno what I'm doing wrong because the code seems ok to me. Any help is appreciated.
#Api(value = "/connectroutes", produces = "application/json", consumes = "application/json",
authorizations = Array(new Authorization(value = "")))
#Produces(Array(MediaType.APPLICATION_JSON))
def routes: Route = {
pathPrefix("sampleroute") {
authenticateBasic(realm = "sample realm", authenticator) { authenticationResult =>
updateA ~
updateB
}
}
#PUT
#Path("{name}")
#Produces(Array(MediaType.APPLICATION_JSON))
#Operation(summary = "sample", description = "UPDATE A",
parameters = Array(new Parameter(name = "name", in = ParameterIn.PATH, description = "updateA name", required = true)),
requestBody = new RequestBody(content = Array(new Content(schema = new Schema(implementation = classOf[A]), mediaType = MediaType.APPLICATION_JSON))),
)
def updateA: Route = {
path(Segment) { name =>
put {
entity(as[A]) { a: A => {
complete(updateAMethod(name, a))
}
}
}
}
}
#PUT
#Path("updateb")
#Produces(Array(MediaType.APPLICATION_JSON))
#Authorization("basicAuth")
#Operation(summary = "Sample", description = "Update B",
requestBody = new RequestBody(content = Array(new Content(schema = new Schema(implementation = classOf[String]), mediaType = MediaType.APPLICATION_JSON))),
)
def updateB: Route = {
path("updateb" / Segment) { namespace =>
put {
entity(as[String]) { updatedval: String => {
complete(updatedVal))
}
}
}
}
}

Scalatra params not working on HTTP POST request

im getting "" for username and country_code with the correct request body but params are working fine in GET request so whats the problem here?
post("/api/clients") {
try {
val username = params.getOrElse("username", "")
val country_code = params.getOrElse("country_code", "")
val client = new Client(username, country_code)
if (client.getId == 0) {
db.save(client)
Ok(client.getId)
} else {
BadRequest("Bad Request")
}
} catch {
case e: Exception => BadRequest("Bad Request")
}
}

How can I prevent some code duplication for a feed handler

I have a feed handler and I would like to prevent the data handle again if the value updated is same as previous value, so I have code like this
map.get(statisticsOpenAcronym).map { x =>
var open = x.asInstanceOf[Number].doubleValue();
if (statistics.open != open) {
statistics.open = open;
statistics.changed = true;
}
}
map.get(statisticsCloseAcronym).map { x =>
var close = x.asInstanceOf[Number].doubleValue();
if (statistics.close != close) {
statistics.close = close;
statistics.changed = true;
}
}
But then I need to have the [if xxx is difference, the update xxx and set change flag ] replicate again and again.
Just wonder, how can I prevent this?
You can compose functions (I had to change some things because I'm not sure of your types):
case class Statistic() {
var open = true
var close = !open
var changed = false
}
var statistics = Statistic()
def f(map: Map[String, String], statisticsOpenAcronym: String, statistic: Statistic, condition: Statistic => Boolean, exec: (Statistic, Boolean) => ()) = {
map.get(statisticsOpenAcronym).map { x =>
val close = x.asInstanceOf[Boolean]
if (condition(statistic)) {
exec(statistic, close)
}
}
}
def closeCondition(): Statistic => Boolean = { statistics: Statistic =>
statistics.close
}
def closeStatistic(): (Statistic, Boolean) => Unit = { (statistics: Statistic, close: Boolean) =>
statistics.close = close
statistics.changed = true;
}
f(Map.empty, "acronym", statistics, closeCondition(), closeStatistic())
Better yet you could make closeStatistics return a new statistic with the parameters you want instead of Unit.
The open case is analogue.

Setting state in ajax success in scalajs-react

I am trying to open one popup on successful submission of another in scalajs-react. My problem is on success state is not getting modified. Here is my addNewAgent method called as a callback on submission of first popup.
def addNewAgent(userModel: UserModel, addNewAgent: Boolean = false): Callback = {
println(addNewAgent)
if(addNewAgent){
createUser(userModel).onComplete {
case Success(s) =>
println(s.msgType)
if (s.msgType == ApiResponseMsg.CreateUserWaiting){
t.modState(s => s.copy(showNewAgentForm = false, showConfirmAccountCreation = true))
} else {
t.modState(s => s.copy(showNewAgentForm = false, showRegistrationFailed = true))
}
case Failure(s) =>
println(s)
t.modState(s => s.copy(showRegistrationFailed = true))
// now you need to refresh the UI
}
t.modState(s => s.copy(showNewAgentForm = false))
} else {
t.modState(s => s.copy(showNewAgentForm = false))
}
}
and component code is :
val component = ReactComponentB[Props]("AddNewAgent")
.initialState(State()) // initial state from TodoStore
.backend(new Backend(_))
.renderPS(($, P, S) => {
val B = $.backend
<.div()(
Button(Button.Props(B.addNewAgentForm(), CommonStyle.default, Seq(HeaderCSS.Style.SignUpBtn)),"Sign Up"),
if (S.showNewAgentForm) NewAgentForm(NewAgentForm.Props(B.addNewAgent))
else if (S.showConfirmAccountCreation ) ConfirmAccountCreation(ConfirmAccountCreation.Props(B.confirmAccountCreation))
else
Seq.empty[ReactElement]
)
})
// .componentDidMount(scope => scope.backend.mounted(scope.props))
.configure(OnUnmount.install)
.build
def apply(props: Props) = component(props)
For anyone looking for an answer to this problem what is happening here is that
the modState calls inside the future completion will never be really called
because they return a Callback that don't run. To solve this you need to add runNow() like t.modState(s => s.copy(showNewAgentForm = false, showRegistrationFailed = true)).runNow()

How to structure a RESTful API with Spray.io?

When I'm using Spray.io to develop a RESTful API, how should I structure my application?
I already saw this answer on how to split a Spray application, but I'm not satisfied with it, since it doesn't seem to use the "one actor per request" approach. Can I forward requests from the root actor to other actors in my application based on paths and, inside these actors, define the related routes?
Thanks
You can certainly forward requests from one actor to another, based on paths or whatever else. Check out my example project (which is a fork of a fork of an example project):
https://github.com/gangstead/spray-moviedb/blob/master/src/main/scala/com/example/routes/ApiRouter.scala
Relavent code from the main actor that receives all requests and routes them to other actors that handle each service:
def receive = runRoute {
compressResponseIfRequested(){
alwaysCache(simpleCache) {
pathPrefix("movies") { ctx => asb.moviesRoute ! ctx } ~
pathPrefix("people") { ctx => asb.peopleRoute ! ctx }
} ~
pathPrefix("login") { ctx => asb.loginRoute ! ctx } ~
pathPrefix("account") { ctx => asb.accountRoute ! ctx }
}
}
And for example the movies route:
def receive = runRoute {
get {
parameters('query, 'page ? 1).as(TitleSearchQuery) { query =>
val titleSearchResults = ms.getTitleSearchResults(query)
complete(titleSearchResults)
}~
path(LongNumber) { movieId =>
val movie = ms.getMovie(movieId)
complete(movie)
}~
path(LongNumber / "cast") { movieId =>
val movieCast = ms.getMovieCast(movieId)
complete(movieCast)
}~
path(LongNumber / "trailers") { movieId =>
val trailers = ms.getTrailers(movieId)
complete(trailers)
}
}
}
I was struggling a lot with creating first full REST project. The examples I've found was on hello world level... I've read few blogs, few comments and I decided to create example project. It is based on scala/akka/spray/mysql
It full working example with websocket to notify clients that data was changed etc. You can check it out on https://github.com/vixxx123/scalasprayslickexample
Here is sample code of routing from that project:
val personCreateHandler = actorRefFactory.actorOf(RoundRobinPool(2).props(Props[CreateActor]), s"${TableName}CreateRouter")
val personPutHandler = actorRefFactory.actorOf(RoundRobinPool(5).props(Props[UpdateActor]), s"${TableName}PutRouter")
val personGetHandler = actorRefFactory.actorOf(RoundRobinPool(20).props(Props[GetActor]), s"${TableName}GetRouter")
val personDeleteHandler = actorRefFactory.actorOf(RoundRobinPool(2).props(Props[DeleteActor]), s"${TableName}DeleteRouter")
val userRoute =
pathPrefix("person") {
pathEnd {
get {
ctx => personGetHandler ! GetMessage(ctx, None)
} ~
post {
entity(as[Person]) {
entity =>
ctx => personCreateHandler ! CreateMessage(ctx, entity)
}
}
} ~
pathPrefix (IntNumber){
entityId => {
pathEnd {
get {
ctx => personGetHandler ! GetMessage(ctx, Some(entityId))
} ~ put {
entity(as[Person]) { entity =>
ctx => personPutHandler ! PutMessage(ctx, entity.copy(id = Some(entityId)))
}
} ~ delete {
ctx => personDeleteHandler ! DeleteMessage(ctx, entityId)
} ~ patch {
ctx => personPutHandler ! PatchMessage(ctx, entityId)
}
}
}
}
}
And sample from create actor handler:
override def receive: Receive = {
case CreateMessage(ctx, person) =>
val localCtx = ctx
connectionPool withSession {
implicit session =>
try {
val resId = PersonsIdReturning += person
val addedPerson = person.copy(id = Some(resId.asInstanceOf[Int]))
localCtx.complete(addedPerson)
publishAll(CreatePublishMessage(TableName, localCtx.request.uri + "/" + addedPerson.id.get, addedPerson))
L.debug(s"Person create success")
} catch {
case e: Exception =>
L.error(s"Ups cannot create person: ${e.getMessage}", e)
localCtx.complete(e)
}
}
}
There are still two important things missing: oauth2 and push notifications to specific user/connection via websocket