How to get array from a json formatted form in Scala? - 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?

Related

how to apply Form validators on object

I have an API in Scala. When I want to create a new User, i need validators on some fields (UserName, FirstName, LastName). I can't find a solution to apply the Form on my case class User. I thought using (User.apply) this will override automatically my User class, but this isn't happening.
User.scala
case class User(
id: Int = 0,
userName: String,
firstName: String,
lastName: String
)
object User {
import play.api.libs.json._
implicit val jsonWrites = Json.writes[User]
implicit val userReads = Json.reads[User]
def tupled = (User.apply _).tupled
}
// Form validator for post requests
object UserForm {
val form: Form[User] = Form(
mapping(
"id" -> number,
"userName" -> text(minLength = 5),
"firstName" -> text(minLength = 5),
"lastName" -> nonEmptyText
)(User.apply)(User.unapply)
)
}
UsersController.scala
def create = Action(parse.json) { implicit request => {
val userFromJson = Json.fromJson[User](request.body)
userFromJson match {
case JsSuccess(user: User, path: JsPath) => {
var createdUser = Await.result(usersRepository.create(user), Duration.Inf)
Ok(Json.toJson(createdUser))
}
case error # JsError(_) => {
println(error)
InternalServerError(Json.toJson("Can not create user"))
}
}
}
}
UsersRepository.scala
def create(user: User): Future[User] = {
val insertQuery = dbUsers returning dbUsers.map(_.id) into ((x, id) => x.copy(id = id))
val query = insertQuery += user
db.run(query)
}
(UsersRepository doesn't matter so much in this problem)
So, when I read the json, I think there need to apply the form validators
val userFromJson = Json.fromJson[User](request.body)
but nothing happening. Another problem is if I send from client a null parameter, will get an error, can not read and create an User.
For example :
"firstName": "" - empty userName will pass
"firstName": null - will fail
This depends on how is configured the User class in client (Angular), with the field null or empty in constructor. I prefer to set it null, even if is a string, if is not a required field (better NULL value in database, than an empty cell)
Try to use this:
request.body.validate[User]
One other thing, you shouldn't use
var createdUser = Await.result(usersRepository.create(user), Duration.Inf)
So use val´s over var´s and the other thing, you don't need to force the future to resolve, use it like this:
val createdUser = usersRepository.create(user)
createdUser.map(Ok(Json.toJson(_)))
And your action should be async.
Find a solution, my Form need to be part of object User, and not to be another object.
object User {
import play.api.libs.json._
implicit val jsonWrites = Json.writes[User]
implicit val userReads = Json.reads[User]
val form: Form[User] = Form(
mapping(
"id" -> number,
"userName" -> text(minLength = 2),
"firstName" -> text(minLength = 4),
"lastName" -> nonEmptyText
)(User.apply)(User.unapply)
)
def tupled = (User.apply _).tupled
}
and the method in Controller became :
def create = Action(parse.json) { implicit request => {
User.form.bindFromRequest.fold(
formWithErrors => BadRequest(Json.toJson("Some form fields are not validated.")),
success => {
val userFromJson = Json.fromJson[User](request.body)
userFromJson match {
case JsSuccess(user: User, path: JsPath) => {
val createdUser = Await.result(usersRepository.create(user), Duration.Inf)
Ok(Json.toJson(createdUser))
}
case error#JsError(_) => {
println(error)
InternalServerError(Json.toJson("Can not create user"))
}
}
}
)
}
}
I know, I need to format my code ... using IntelliJ is so annoying after Visual Studio :(

Async Action and Future

I'm using Play 2.5 and Scala 2.11. I'm trying to use async Action with Future to return json string but can't get it to work.
Here is my action :
def getData = Action.async { implicit request =>
val futureData = Future { daoObj.fetchData }
futureData onComplete {
case Success(data) =>
Ok(Json.prettyPrint(Json.obj("data" -> data.groupBy(_.name))))
case Failure(t) =>
BadRequest(Json.prettyPrint(Json.obj(
"status" -> "400",
"message" -> s"$t"
)))
}
}
The daoObj.fetchData returns scala.concurrent.Future[List[models.Data]]
Here is the models.Data :
case class Data(name: String, details: String)
I can have data like this
Data (name = "data1", details = "details1")
Data (name = "data1", details = "details1a")
Data (name = "data2", details = "details2")
that I can join on name to return a structure of the form
Map(data1 -> List("details1", "details11"),
data2 -> List("details2"))
I have a compilation error on groupBy :
value groupBy is not a member of scala.concurrent.Future[List[models.Data]]
1) How to get the value (List[models.Data]) from the Future in my action ?
2) It's my first Play Scala app so if you have any comment to improve my code, it's welcome.
You should not use Future.onComplete, which is a callback (with side-effect), but .map and .recover to turn your Future[T] into a Future[Result] which is what's expected by Action.async.
def getData = Action.async { implicit request =>
Future { daoObj.fetchData }.map { data =>
Json.obj("data" -> data.groupBy(_.name))
}.recover {
case error => BadRequest(Json.obj(
"status" -> "400",
"message" -> error.getMessage
))
}
}
There is no need to pretty-print the JsObject which can be directly written as result (except for debugging purposes maybe).

Insert item in DB with two columns using Scala and Play Framework

I've followed this application tutorial. Basically, in this app they create a todo item. I've replicated the model to a survey application, and I want to create a question that contains both question text and question type (binary, text entry etc). I get various degrees of errors regarding my question form formatting, etc but the main problem seems to be in the newQuestion action on
questionForm.bindFromRequest.fold(errors=> ..., question=> ...)
My code is formatted as follows:
QuestionController.scala:
def questions = Action {
Ok(views.html.question(Question.all(), questionForm))
}
def newQuestion = Action { implicit request =>
questionForm.bindFromRequest.fold(
errors => BadRequest(views.html.question(Question.all(), errors)),
question => {
Question.create(question)
Redirect(routes.QuestionController.questions)
}
)
}
def deleteQuestion(id: Long) = Action {
Question.delete(id)
Redirect(routes.QuestionController.questions)
}
val questionForm = Form(
"questiontext" -> nonEmptyText
)
CaseClass/companion object Question.scala:
case class Question (id:Long, questionText:String, questionType:String)
object Question {
val question = {
get[Long]("id") ~
get[String]("questiontext") ~
get[String]("questiontype") map {
case id~questiontext~questiontype => Question(id, questiontext,questiontype)
}
}
def all(): List[Question] = DB.withConnection { implicit c =>
SQL("select * from questions").as(question *)
}
def create(text:String) {
DB.withConnection {
implicit c =>
SQL("insert into questions(questiontext) values ({text})").on(
'text -> text
//'quesType -> qType
).executeUpdate()
}
}
def delete(id:Long) {
DB.withConnection{ implicit c =>
SQL("delete from questions where id = {id}").on(
'id -> id
).executeUpdate()
}
}
}
I essentially want to run create with two parameters as such:
def create(text:String,qType:String) {
DB.withConnection { implicit c =>
SQL("insert into questions(questiontext,questiontype) values ({text},{quesType})").on(
'text -> text,
'quesType -> qType
).executeUpdate()
}
}
But I'm unsure how to handle this in the bindFromRequest.fold as mentioned previously.
I tried editing the questionForm to use a mapping and using (Question.apply)(Question.unapply) as well as just a tuple and the questiontext -> nonEmptyText and questionType -> nonEmptyText contained in that tuple with no luck.
I edited the scala.html file to contain the right type of value accordingly with still no luck.
I can get it to save with one column but it crashes because I defined the questionType to be a required field in pgsql and the get fails on the ~get questionType from * after appending without a question type.
UPDATE:
I've edited it to try and emulate the code here:
https://www.playframework.com/documentation/2.4.1/ScalaForms
So now, I am using the following code:
def questions = Action {
Ok(views.html.question(Question.all(), questionForm))
}
def newQuestion = Action { implicit request =>
logger.error(s"wtf")
questionForm.bindFromRequest.fold(
errors => {BadRequest(views.html.question(Question.all(), errors))
},
question => {
Question.create(question:Question)
Redirect(routes.QuestionController.questions).flashing("success"->"Question saved successfully!")
}
)
}
def deleteQuestion(id: Long) = Action {
Question.delete(id)
Redirect(routes.QuestionController.questions)
}
val questionForm:Form[Question]=Form(mapping(
"id" -> longNumber,
"versionid" -> longNumber,
"questiontext" -> nonEmptyText,
"questiontype" -> nonEmptyText)(Question.apply)(Question.unapply)
)
I've updated my question.scala.html file to take #(questions: List[Question], questionForm: Form[(Question)]) as inputs.
when i hit submit, I keep getting the Log message of :wtf
Following the example i most recently linked, I am not understanding why it does not go to the case of question and continue to create the question in the database. I updated my database create function as the following as well:
def create(question:Question) {
DB.withConnection {
implicit c =>
SQL("insert into questions(questiontext,questiontype,versionid) values ({text},{quesType},{versId})").on(
'text -> question.questionText,
'quesType -> question.questionType,
'versId -> 1.toLong
).executeUpdate()
}
}
Thanks for the help thus far, I hope I can figure out what I am doing wrong shortly.

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

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.

Form binding with custom mapping to object - how?

I have these case class
case class Blog(id:Long, author:User, other stuff...)
case class Comment(id:Long, blog:Blog, comment:String)
and a form on the client side that submits the data
blog_id:"5"
comment:"wasssup"
I'm writing some simple code to let a user add a comment to a blog.
The user is logged in so the his user_id is not needed from the client side, we know who he is...
I would like to bind the blog_id to a Blog object loaded from db, and if it doesn't exist show an error.
The examples on play framework docs are not helpful.
They only show mappings for forms that represent a single Object and all of its fields.
Here I'm representing a tuple of a (b:Blog, comment:String) and for the Blog I'm only supplying it's id.
I'd like to have a mapping that would provide me with the conversion + validation + error messages, so i can write something like:
val form = Form(
tuple(
"blog_id" -> blogMapping,
"comment" -> nonEmptyText
)
)
form.bindFromRequest().fold(...
formWithErrors => {...
}, {
case (blog, comment) => {do some db stuff to create the comment}
...
The "blogMapping" wlil work like other mappings, it will bind the posted data to an object, in our case a blog loaded from db, and in case it's not successful it will provide an error that we can use on the formWithErrors => clause.
I'm not sure how to accomplish this, the docs are kinda lacking here...
any help is appreciated!
I ended up looking at how playframwork's current bindings look like and implementing something similar, but for Blog:
implicit def blogFromLongFormat: Formatter[Blog] = new Formatter[Blog] {
override val format = Some(("Blog does not exist", Nil))
def bind(key: String, data: Map[String, String]) = {
scala.util.control.Exception.allCatch[Long] either {
data.get(key).map(s => {
val blog_id = s.toLong
val blog = Daos.blogDao.retrieve(blog_id)
blog.map(Right(_)).getOrElse(Left(Seq(FormError(key, "Blog not found", Nil))))
}).get
} match {
case Right(e:Either[Seq[FormError],Blog]) => e
case Left(exception) => Left(Seq(FormError(key, "Invalid Blog Id", Nil)))
case _ => Left(Seq(FormError(key, "Error in form submission", Nil)))
}
}
def unbind(key: String, value: Blog) = Map(key -> value.id.toString)
}
val blogFromLongMapping: Mapping[Blog] = Forms.of[Blog]
To me, this doesn't really look like a binding problem.
The issue is around the Model-View-Controller split. Binding is a Controller activity, and it's about binding web data (from your View) to your data model (for use by the Model). Querying the data, on the other hand, would very much be handled by the Model.
So, the standard way to do this would be something like the following:
// Defined in the model somewhere
def lookupBlog(id: Long): Option[Blog] = ???
// Defined in your controllers
val boundForm = form.bindFromRequest()
val blogOption = boundForm.value.flatMap {
case (id, comment) => lookupBlog(id)
}
blogOption match {
case Some(blog) => ??? // If the blog is found
case None => ??? // If the blog is not found
}
However, if you are determined to handle database lookup in your binding (I'd strongly advise against this, as it will lead to spaghetti code in the long run), try something like the following:
class BlogMapping(val key: String = "") extends Mapping[Blog] {
val constraints = Nil
val mappings = Seq(this)
def bind(data: Map[String, String]) = {
val blogOpt = for {blog <- data.get(key)
blog_id = blog.toLong
blog <- lookupBlog(blog_id)} yield blog
blogOpt match {
case Some(blog) => Right(blog)
case None => Left(Seq(FormError(key, "Blog not found")))
}
}
def unbind(blog: Blog) = (Map(key -> blog.id.toString), Nil)
def withPrefix(prefix: String) = {
new BlogMapping(prefix + key)
}
def verifying(constraints: Constraint[Blog]*) = {
WrappedMapping[Blog, Blog](this, x => x, x => x, constraints)
}
}
val blogMapping = new BlogMapping()
val newform = Form(
tuple(
"blog_id" -> blogMapping,
"comment" -> nonEmptyText
)
)
// Example usage
val newBoundForm = newform.bindFromRequest()
val newBoundBlog = newBoundForm.get
The main thing we've done is to create a custom Mapping subclass. This can be a good idea under some circumstances, but I'd still recommend the first approach.
You can do it all in the form definition.
I have made some simple scala classes and objects from your example.
models/Blog.scala
package models
/**
* #author maba, 2013-04-10
*/
case class User(id:Long)
case class Blog(id:Long, author:User)
case class Comment(id:Long, blog:Blog, comment:String)
object Blog {
def findById(id: Long): Option[Blog] = {
Some(Blog(id, User(1L)))
}
}
object Comment {
def create(comment: Comment) {
// Save to DB
}
}
controllers/Comments.scala
package controllers
import play.api.mvc.{Action, Controller}
import play.api.data.Form
import play.api.data.Forms._
import models.{Comment, Blog}
/**
* #author maba, 2013-04-10
*/
object Comments extends Controller {
val form = Form(
mapping(
"comment" -> nonEmptyText,
"blog" -> mapping(
"id" -> longNumber
)(
(blogId) => {
Blog.findById(blogId)
}
)(
(blog: Option[Blog]) => Option(blog.get.id)
).verifying("The blog does not exist.", blog => blog.isDefined)
)(
(comment, blog) => {
// blog.get is always possible since it has already been validated
Comment(1L, blog.get, comment)
}
)(
(comment: Comment) => Option(comment.comment, Some(comment.blog))
)
)
def index = Action { implicit request =>
form.bindFromRequest.fold(
formWithErrors => BadRequest,
comment => {
Comment.create(comment)
Ok
}
)
}
}