Grails: change the property name "errors" - validation

I have a class that looks like this
#Validateable
class StudentBean{
def String name;
def String age;
def String address;
def List<ErrorBean> errors = [];
static constraints = {
age nullable : false, validator : { val, obj, errors->
if(val<10)
errors.rejectValue("age", "student.age.notQualified.message", [val] as Object[], "Student not qualified.");
}
}
}
You can see that I have a errors property in StudentBean (List). i realized that variable name has a conflict with the errors where the bean error is stored. What i did was changed the closure to something like this
age nullable : false, validator : { val, obj, errorsValue->
if(val<10)
errorsValue.rejectValue("age", "student.age.notQualified.message", [val] as Object[], "Student not qualified.");
}
but I couldnt loop the errorsValue to get the error.
When I check what errors is this is what docs says:
The errors property on domain classes is an instance of the Spring
Errors interface. The Errors interface provides methods to navigate
the validation errors and also retrieve the original values.
https://grails.github.io/grails-doc/latest/guide/validation.html
so this is the property of the domain class, probably declared but hidden.
The question is can I change the errors property name to something else?

You can not change the validation errors property.
The reason for this comes down to the philosophy behind Grails. errors exists on all Grails domain classes by convention. Much of what makes Grails powerful is not the configuration but rather the "convention over configuration". This ensures consistency between Grails projects and makes the entire framework simpler to learn and use.

Related

In Gradle, how do you perform validation of lazily evaluated properties (on extensions)?

Is there a way to validate a property value when the property is evaluated? I can't do it in the getter because that returns the Property object - I want the validation to run only when the actual value is calculated (i.e. I want to be lazy evaluation friendly).
They show extensions using the Property object here:
https://docs.gradle.org/current/userguide/lazy_configuration.html#connecting_properties_together
However, they don't explain how to do property validation when the value is calculated. Here is the snipet of code from the Gradle documentation provided example:
// A project extension
class MessageExtension {
// A configurable greeting
final Property<String> greeting
#javax.inject.Inject
MessageExtension(ObjectFactory objects) {
greeting = objects.property(String)
}
}
If I wanted to make sure the value of greeting was not equal to test, then how would I do that when it is evaluated?
For most use cases, it should be sufficient to just validate the property value once you resolve it in your task or in other internal parts of your plugin. Only a few extensions are actually designed to be consumed by other plugins or the build script.
Gradle does not provide some validation that can be attached to a property, however you can build this functionality on your own like in the example below:
class MessageExtension {
private final Property<String> _greeting
final Provider<String> greeting
#javax.inject.Inject
MessageExtension(ObjectFactory objects) {
_greeting = objects.property(String)
greeting = _greeting.map { value ->
if (value.equals('test'))
throw new RuntimeException('Invalid greeting')
return value
}
}
def setGreeting(String value) {
_greeting.set(value)
}
def setGreeting(Provider<String> value) {
_greeting.set(value)
}
}
project.extensions.create('message', MessageExtension)
message {
greeting = 'test'
}
println message.greeting.get()
I turned the Property into a backing field for a Provider that runs the validation when resolved. If you do not want to throw an exception, but just return an empty Provider, you may replace the map with a flatMap.

Spring boot 2.1.0 security change with kotlin data class?

This problem make me physically ill.
Joke aside, I've been trying to add an authentication layer to my web app using spring-boot with security plugin. Here is my data class.
#Document(collection = "user")
data class User (
var name : String,
var password : String,
var email : String,
var type : String,
var status : String,
var balance : Int
){
#Id val id : String = ObjectId.get().toHexString()
}
After some searching, Ctr+C, Ctr+V, I'm successfully set-up some custom authentication that will get user information from database, look like this:
override fun loadUserByUsername(name : String): UserDetails {
logger.info(name)
val user = repo.findByName(name)
return User(user!!.name,passwordEncoder.encode(user.password),AuthorityUtils.NO_AUTHORITIES)
}
Here where the fun begin, its seem that the code never run pass val user = repo.findByName(name). Worst thing is, there are no exception being thrown, the code run to that line and the rest just disappear.
Out of frustration, I decide to fake the return object so that I can get pass the authentication like this:
override fun loadUserByUsername(name : String): UserDetails {
logger.info(name)
//val user = repo.findByName(name)
logger.debug("asdkfhasdklfjhasdf")
return User("string",passwordEncoder.encode("you"),AuthorityUtils.NO_AUTHORITIES)
}
Now, finally I can get some exception:
{
"timestamp": "2018-11-08T18:08:29.541+0000",
"status": 500,
"error": "Internal Server Error",
"message": "No accessor to set property #org.springframework.data.annotation.Id()private final java.lang.String com.sonnbh.jwt.User.id!",
"path": "/user"
}
The exception state that spring cannot access property id so I change the type of id from val to var.
#Document(collection = "user")
data class User (
var name : String,
var password : String,
var email : String,
var type : String,
var status : String,
var balance : Int
){
#Id var id : String = ObjectId.get().toHexString()
}
Finally, my app work as expected. However, after some attempt trying to dig deeper to the problem, I found that this problem only occur to spring-boot v2.1.0. My old project which use spring-boot v2.0.5 actually run fine with val id. This led me to some question:
Did I my old implement of data class User properly? I just want to prevent any change to User.id after its being read from database or init. What can I do to improve?
Why spring-boot v2.1 can't access to the property like spring-boot v2.0.5 did?
Spring Data in 2.1. has changed the way in which it deals with final fields in entities. It no longer uses reflection to override the immutability of the fields, which in general is good. There are a few ways to cope with the problem.
They are described here: https://jira.spring.io/browse/DATACMNS-1374?focusedCommentId=182289&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-182289
Here's what the Spring guys recommend:
Add a #PersistenceConstructor to construct the entity that sets immutable fields.
Add wither methods (MyEntity withXxx(…)) to create a new instance that contains the changed property value.
Alternatively: Use Kotlin's data class feature. This will basically do the same as wither methods.
Can only answer the first part; you could try moving the declaration of ID to be apart of the constructor? That will satisfy your requirement of only initialising when the object is created and it will still be read only.

Grails: Call a localization message from within another message

Coming from Struts/XWorks I was able to do something like this
Messages.properties
myMessage.invalid.fieldvalue=Invalid input for $'{'getText("{0}")'}'
email.label=Email Address
Application Code
getText("myMessage.invalid.fieldvalue",new String[] {"email.label"})
Output: Invalid input for Email Address
So basically the parameter I am passing into a message is actually the code for another message and I want to render them both together.
I can't seem to find a way to do this with Grails/Spring messaging. Is this possible and if so how?
EDIT:
To more clearly show one of the reason I am asking for this take this example.
Lets say I have 5 Domain classes with the property emailAddress
To validate for NULL I would have to do this
myClass1.emailAddress.nullable=Email Address cannot be NULL
myClass2.emailAddress.nullable=Email Address cannot be NULL
myClass3.emailAddress.nullable=Email Address cannot be NULL
myClass4.emailAddress.nullable=Email Address cannot be NULL
myClass5.emailAddress.nullable=Email Address cannot be NULL
What I want to be able to do is simply the messaging by overriding the default validation message as such
OLD: default.null.message=Property [{0}] of class [{1}] cannot be null
NEW: default.null.message=getMessage({0}) cannot be null
emailAddress=Email Address
So now anytime any class has a property called emailAddress and it validates as NULL I will get the message Email Address cannot be null. There is no need to have 5 messages that basically say the same exact thing. If I had another class with the property emailAddress then its already handled and I dont have to copy and paste a 6th line.
Anytime I have classes with shared property names, all I have to do is add just add a single line for each property that will be applied to all classes
sharedProp1= Shared property 1
sharedProp2= Shared property 2
When in a controller, call message for the param you want to internationalize and store that in a local variable.
Then call message for the full message and add your local variable, with the internationalized param value, as a param to that message.
def emailLabel = message(code: "email.label")
def fullMessage = message(code: 'myMessage.invalid.fieldvalue', args: [emailLabel])
Your messages.properties would contain something like
myMessage.invalid.fieldvalue=Invalid input for {0}
email.label=Email Address
I was able to get this done by extending the MessageSource and overriding the resolveArguments method.
class CustomMessageSource extends ReloadableResourceBundleMessageSource {
#Override
protected Object[] resolveArguments(Object[] args, Locale locale) {
if (args == null) {
return new Object[0];
}
List<Object> resolvedArgs = new ArrayList<Object>(args.length);
for (Object arg : args) {
if (arg instanceof MessageSourceResolvable) {
resolvedArgs.add(getMessage((MessageSourceResolvable) arg, locale));
}
else {
//resolvedArgs.add(arg) **REPLACED THIS LINE
resolvedArgs.add(getMessage(arg, null, arg, locale));
}
}
return resolvedArgs.toArray(new Object[resolvedArgs.size()]);
}
}
I replaced a single line within the loop that evaluates your message arguments. I basically take the argument and see if its a key to another message. If yes, then replace the argument with that message, if no then continue as normal to use the argument
Make sure to map the new messageSource in your resources.groovy file
beans = {
messageSource(groovyUtils.CustomMessageSource) {
basenames = "messages"
}
}

spring-data-mongodb Query.fields().slice() on #DBRef field throws MappingException

I have a problem with sliced access to some #DBRef field in my model. I use spring-data-mongodb-1.8.0.M1.jar
The model is like:
class Model {
....
#DBRef
List<OtherModel> members;
...
}
and I need sliced access to the members variable.
I use this query:
ObjectId objectId = new ObjectId("55c36f44f359d8a455a21f68");
Query query = new Query(Criteria.where("_id").is(objectId));
query.fields().slice("members", pageable.getOffset(), pageable.getPageSize());
List<Model> models = mongoTemplate.findOne(query, Model.class);
But I get this exception:
org.springframework.data.mapping.model.MappingException: No id property found on class class [Ljava.lang.Integer;
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.createDBRef(MappingMongoConverter.java:842)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.toDBRef(MappingMongoConverter.java:329)
at org.springframework.data.mongodb.core.convert.QueryMapper.createDbRefFor(QueryMapper.java:460)
at org.springframework.data.mongodb.core.convert.QueryMapper.convertAssociation(QueryMapper.java:417)
at org.springframework.data.mongodb.core.convert.QueryMapper.convertAssociation(QueryMapper.java:378)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedKeyword(QueryMapper.java:257)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObjectForField(QueryMapper.java:200)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:123)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:1647)
at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:563)
at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:558)
where a field
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
is set. It checks against isExists, but not against something like isSliced (which does not yet exist) and therefore is evaluated to true and, as a cause, tries to convert the non-existing association which is, in this case, just the slice-directive (an integer array). When I set the variable needsAssociationConversion to false while debugging, as if a kind of keyword.isSlice() check was done, everything works fine.
Is this a bug?
Executable project is here
https://github.com/zhsyourai/sliceDemo

Use of "#this" in Moles delegates

When I set a property of a moled type, it looks like they always require, as the first parameter, an object of the original moled type. I also noticed that some of the examples in the Moles Reference Guide assign this parameter as #this and I am trying to figure out why.
For instance, the original class looks something like this:
public class ProductDAO
{
internal void Insert(Product product, string id)
{
...
}
}
When I go to mole this method, the property is expecting a delegate whose first parameter is always the type of the moled object, in this case, a ProductDAO object. So in this case, the property is expecting a delegate of:
Action<ProductDAO, Product, string>
So do I always have to provide that moled object as the first parameter of my lambda expression? If so, what's the difference in using a regular variable name versus #this? What does #this mean/do?
MProductDAO.AllInstances.InsertProductString = (dao, product, id) => { };
versus
MProductDAO.AllInstances.InsertProductString = (#this, product, id) => { };

Resources