Access protobuf field name dynamically with scala - scala

I am new to Scala. Writing my first application.
I have defined my proto file with fields email_id and phone_number which is request definition for grpc call
I can access values by dot operator like params.emailId
Now what I am trying to do is I have one array of mandatory fields. I want to check the values for those fields defined in an array with input request parameters.
How can i access this params.{field name from array} to check for not empty values.
Getting error for below code with :
val mandatoryFields = Array("emailId","phoneNumber")
println(params.emailId) //works
for (fields <- mandatoryFields) {
println(fields)
println(params.fields) // getting error
}
It has function 'in.getFieldByNumber()' where you can fetch value by index location, is there any function available like getFieldByName() or something like that.

Although it's been a long from the question date, I don't think it is answerless. Actually, I have one:
Using toPMessage method, you'll have your protobuffer case class as an instance of PMessage object. Then you could retrieve the Map[FieldDescriptor, PValue]. Finding field values by name would be like:
val fieldDescriptorPValueMap: Map[FieldDescriptor, PValue] = params.toPMessage.value
mandatoryFields.foreach(fieldName=>{
println(
fieldDescriptorPValueMap
.filter(entry => fieldName == entry._1.name)
.values
.head.as[String]
)
})

Related

Play Framework request attributes with typed key

I seem to have issues accessing the attributes of the request attributes map in Play. Following the explanation offered by Play (Link), I should get the correct data from the attributes, but the Option is returned as None.
My structure is as follows. One controller (later injected named as "sec") has the typed attribute for shared access to it:
val AuthenticatedAsAttr: TypedKey[AuthenticatedEmail] = TypedKey("AuthenticatedAs")
The type AuthenticatedEmail is defined in the companion object of this controller as a case class:
case class AuthenticatedEmail(email: String)
The filter passes the attribute to the next request:
val attrs = requestHeader.attrs + TypedEntry[AuthenticatedEmail](sec.AuthenticatedAsAttr, AuthenticatedEmail(email))
nextFilter(requestHeader.withAttrs(attrs))
When trying to then access this attribute in another controller, the returned Option is None:
val auth = request.attrs.get(sec.AuthenticatedAsAttr)
I confirmed via println that the value is definitely in request.attrs but run out of options to debug the issue successfully. A fraction of the println output below.
(Request attrs,{HandlerDef -> HandlerDef(sun.misc .... ,POST, ... Cookies -> Container<Cookies(Cookie ... , AuthenticatedAs -> AuthenticatedEmail(a#test.de), ... })
My Scala version is 2.12.6, Play Framework version 2.6.18. Any help is highly appreciated.
It turns out that the TypedKey must be within an object, not an inject-able controller. So moving it to an object like the following resolves the issue:
object Attrs {
val AuthenticatedAsAttr: TypedKey[AuthenticatedEmail] = TypedKey("AuthenticatedAs")
}
The reason is the implementation of TypedKey (Link), which does not contain an equals method and therefore reverts to comparing memory references.

how to create a Form from an object of case class

I am using fold method of form as follows
def regSubmit = Action { implicit request =>
userForm.bindFromRequest.fold({
formWithErrors=>BadRequest(views.html.Error("Registration failed")( formWithErrors.errors))
},
{
userData=>Ok(views.html.regconf("Registration Successful")(**//here I want to send a Form, not data from the form**))
})
How can I create Form from a tuple or single variable, a class or a case class?
userForm will (usually?) be defined as a val, so immutable. It holds the mapping (this field name into a variable in this position of this type, ...) When you use bindFromRequest.fold you are not changing userForm, you are using the mapping information in userForm to generate a new instance of your case class, say userData (or a version of the form with errors in it). Each time you execute that method, you will get a new instance of userData.
userForm.fill(userData) returns a new form instance, a populated instance of the form, so also does not change userForm itself.

#helper.repeat in PlayFramework

I don't understand how to describe #helper.repeat in play2.2.1.
#helper.form(action = routes.Application.send) {
#helper.repeat(
field = form1("input"), min = 3
) {fld =>
#helper.inputText(
field = fld, '_label ->"input"
)
}
}
It is the part fld => #helper.inputText(field = fld) that I can't understand.
What does it mean?
I know Java, so I assume it is a functional writing, but in above code, where does the variable fld come from?
And why the tip of the arrow indicates #helper.inputText(field = fld)?
why is fld the value of field in #helper.inputText?
I have googled, but I couldn't find an enough explanation for a beginner.
I am not sure of Scala's grammar.
Please explain above code for a beginner?
Original Answer
This seems to be a bit overcomplicated. There is no need to assign values by hand. Usually you would write a repeater like this:
#helper.repeat(form1("input"), min = 3) { fld =>
#helper.inputText(fld, '_label ->"input")
}
In functional programming this is a so called higher-order function. You may know other scala built in higher-order functions like map, filter or foreach. #helper.repeat is very similar to foreach. form1("input") refers to a collection of values you want to display. min = 1 tells the repeater to show at least one field. Finally within { fld => ... } you iterate over all values defined in your collection.
In other words: { fld => ... } is just an anonymous function that takes a single Field parameter (fld) and displays a text input for that specific field.
Follow Up
Ok, I'm trying to follow up your questions from the comments. Let's start by the signature of helper.repeat. There is no magic involved here, it's just a regular Scala function:
helper.repeat(field: Field, min: Int)(fieldRenderer: (fld: Field) => Html): Seq[Html]
In Scala, functions can have multiple parameter lists. Here we have two. The first parameter list takes two parameters: field and min. The second parameter list takes a single function as parameter: fieldRenderer. fieldRenderer itself takes again a single parameter (fld) and returns Html.
The important thing is, you are not passing "data" but a function instead. To clear this up:
The signature fieldRenderer: (fld: Field) => Html
is equal to def fieldRenderer(fld: Field): Html
This means, you can do anything within this function, as long as it returns Html. That's exactly what happens in the example at the very top. You pass a Field and return Html. You do that by calling #helper.inputText.
Now repeat first gets a List[Field] from field you pass as first parameter. This list corresponds to the String List of your container class. Also repeat ensures there is at least one element in that list. This is, because you passed 1 as min. Then the function fieldRenderer is applied to all elements in our list.
A pseudo code implementation of repeat could look like this:
def repeat(field: Field, min: Int)(fieldRenderer: (fld: Field) => Html): Seq[Html] {
val list: List[Field] = field.getFields
var output: List[Html] = List()
for (i = 0; i < list.length; i++) {
val element: Field = list.get(i)
val html: Html = fieldRenderer(element)
output += html
}
return output
}

Setting the property of an object using a variable value in scala

I'm trying to create a restful method to update data in the database, I'm using Scala on Play! framework. I have a model called Application, and I want to be able to update an application in the database. So the put request only requires the id of the application you want to update, then the optional properties you want to update.
So in my routes I have this:
PUT /v1/auth/application controllers.Auth.update_application(id: Long)
The method I currently have is this:
def update_application(id: Long) = Action { implicit request =>
var app = Application.find(id)
for((key, value) <- request.queryString) {
app."$key" = value(0)
//app.name = value(0)
}
val update = Application.update(id, app)
Ok(generate(
Map(
"status" -> "success",
"data" -> update
)
)).as("application/json")
}
In the method above I am looping through the request and the app object as a map instance, then updating the app model to be updated using the model. I know there is an easier way is to create the request string as map and just iterate through the object, but I am doing it this way for learning purposes. I'm new to Play! and Scala, barely a week new.
Is there a way to set a property using a variable dynamically that way? In the above method at the end of the loop that is how I would update a object's property in Groovy. So I'm looking for the equivalent in Scala. If Scala can't do this, what is the best way about going about accomplishing this task? Reflection? I don't want to over-complicate things
Play! provides play.api.data.Forms which allows creating a Form that uses the apply() and unapply() methods of a case class. Then you can call form.fill(app) to create a form with the initial state, bindFromRequest(request) to update the form with the request parameters, and get() to obtain a new instance with updated fields.
Define the form only once:
val appForm = Form(
mapping(
"field1" -> text,
"field2" -> number
)(Application.apply)(Application.unapply)
)
Then in your controller:
val app = appForm.fill(Application.find(id)).bindFromRequest(request).get
val update = Application.update(id, app)
Check out Constructing complex objects from the documentation.

Is there a generic way of setting field values in mapper from list of values?

Is there a way of creating method for setting the value in the Model's fields without setting the values explicitly like -
ModelName.create.fieldName1("value").fieldName2("value2") and so on
Can we iterate through all available fields of that model and set their values form some list-of-values ?
something like ...
Model.allFields.foreach((fld)=> {
fld.set(valueList(indx)); indx+=1
}
Actually I want to set values into all models using some generic method that works for all models.
According to my comment:
val list = List(...)
val record = YourRecordClass.createRecord
record.allFields.zip(list).foreach {case(field,value) => field.setFromAny(value)}