Adding the localized Display Name to the constraint violation message? - validation

We are using the new GWT validation library in 2.5.
We are adding an aggregated list of violations to our screen. This list must display the localized field name.
#MyNotNull(foo= "Stage")
public String getStage();
Localized message needs to display
"Stage is a required field"
The message in MyValidationMessages.properties reads
{foo} is a required field
Note that annotations do not allow non-constant values to be assigned to attributes. So we have to get the locale value somehow at design time :/
This will not work
#MyNotNull(foo = injector.getLocale().errorMessage())
public String errorMessage()
How do I use localeKey to look up the locale in the locale files since the property requires a constant?

The solution is to
add something like FieldLocale.properties this is a constants lookup
Add an attribute to your annotation like localeKey
Iterate your ConstraintViolation collection
Use something like the below to get the attribute value
Look up the localized value in your FieldLocale.properties file
Copy the violation and change the message to the localized version
protected String getAttributeValue(ConstraintViolation violation, String key) {
ConstraintDescriptor descriptor = violation.getConstraintDescriptor();
if (descriptor.getAttributes().containsKey(key))
return (String) descriptor.getAttributes().get(key);
return null;
}
protected ConstraintViolation<T> copyMessage(ConstraintViolation<T> violation, String message) {
return ConstraintViolationImpl.<T> builder() //
.setConstraintDescriptor(violation.getConstraintDescriptor()) //
.setInvalidValue(violation.getInvalidValue()) //
.setLeafBean(violation.getLeafBean()) //
.setMessage(message) //
.setMessageTemplate(violation.getMessageTemplate()) //
.setPropertyPath(violation.getPropertyPath()) //
.setRootBean(violation.getRootBean()) //
.setRootBeanClass(violation.getRootBeanClass()) //
.build();
}

Related

MapStruct Spring Page to custom object conversion includes check

I am using MapStruct to convert a Page object to a custom object of my application. I am using this mapping in order to convert the content field of the Page object to a list of custom objects found in my data model:
#Mapping(target = "journeys", source = "content")
While this works OK and does convert the elements when content is present, this does not work correctly in case of no Page content. Taking a look at the code seems to show that the following check is added in the generated mapper class:
if ( page.hasContent() ) {
List<JourneyDateViewResponseDto> list = page.getContent();
journeyDateViewPageResponseDto.setJourneys( new ArrayList<JourneyDateViewResponseDto>( list ) );
}
When this is added the mapping action of the inner objects is omitted, meaning that I end up with a null list. I am not really sure as to why and how this check is added but I would like to find a way of disabling it and simply end up with an empty list of elements. Is there a way this can be done using MapStruct?
MapStruct has the concept of presence checkers (methods that have the pattern hasXXX). This is used to decide if a source property needs to be mapped.
In case you want to have a default value in your object I would suggest making sure that your object is instantiated with an empty collection or provide an #ObjectFactory for your object in which you are going to set the empty collection.
e.g.
Default value in class
public class JourneyDateViewPageResponseDto {
protected List<JourneyDateViewResponseDto> journeys = new ArrayList<>();
//...
}
Using #ObjectFactory
#Mapper
public interface MyMapper {
JourneyDateViewPageResponseDto map(Page< JourneyDateViewResponseDto> page);
#ObjectFactory
default JourneyDateViewPageResponseDto createDto() {
JourneyDateViewPageResponseDto dto = new JourneyDateViewPageResponseDto();
dto.setJourneys(new ArrayList<>());
return dto;
}
}
#Mapping(target = "journeys", source = "content", defaultExpression = "java(java.util.List.of())")

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.

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"
}
}

Web API validation error

I have a View Model called SignUp with the EmailAddress property set like this:
[Required]
[DuplicateEmailAddressAttribute(ErrorMessage = "This email address already exists")]
public string EmailAddress { get; set; }
and the custom validator looks like this:
public class DuplicateEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
PestControlContext _db = new PestControlContext();
int hash = value.ToString().GetHashCode();
if (value == null)
{
return true;
}
if (_db.Users.Where(x => x.EmailAddressHash == hash).Count() > 0)
return false;
else
return true;
}
}
The problem I'm having is that if the user leaves the email address field blank on the sign up form the application is throwing a null reference exception error (I think it's looking for "" in the database and can't find it). What I don't understand is why this isn't being handled by the Required attribute - why is it jumping straight into the custom validator?
The Required attribute would have resulted in an error being added to the model state. It will not short-circuit the execution though. The framework continues to run other validators for the simple reason that all the errors about the request need to be sent out in a single shot. Ideally, you wouldn't want the service to say something is wrong to start with and when the user re-submits the request after making a correction, the service comes back and say some other thing is wrong and so on. It will be an annoyance, I guess.
The NullReferenceException is thrown because value.ToString() is called before the check against null. As you need the hash variable only after the check, you can solve this by reordering the statements:
if (value == null)
{
return true;
}
int hash = value.ToString().GetHashCode();
In addition, you could also move the PestControlContext after the check against null and use a using statement to dispose of it properly.
As also #Baldri pointed out, each validator can add Error messages and all of them are run, even if a previous one already signaled the data to be invalid. Furthermore, I'd not rely on that the validations are run in the order that you specify when marking the property with the attributes (some frameworks implement their own attribute ordering mechanism in order to assert that the order is deterministic, e.g. priorities or preceding attributes).
Therefore, I suggest reordering the code in the custom validator is the best solution.

How do I store a comma-separated list in Orchard CMS?

Using Orchard CMS, I am dealing with a record and a part proxy, but cannot figure out how to save it into the DB. In fact, I confess I don't even know how to get the items I'm trying to save into this paradigm. I was originally using enum's for choices:
MyEmum.cs:
public enum Choices { Choice1, Choice2, Choice3, Choice4 }
MyRecord.cs:
public virtual string MyProperty { get; set; }
MyPart.cs:
public IEnumerable<string> MyProperty
{
get
{
if (String.IsNullOrWhiteSpace(Record.MyProperty)) return new string[] { };
return Record
.MyProperty
.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim())
.Where(r => !String.IsNullOrEmpty(r));
}
set { Record.MyProperty = value == null ? null : String.Join(",", value); }
}
Now, in my service class, I tried something like:
public MyPart Create(MyPartRecord record)
{
MyPart part = Services.ContentManager.Create<MyPart>("My");
...
part.MyProperty = record.MyProperty; //getting error here
...
return part;
}
However, I am getting the following error: Cannot implicitly convert 'string' to System.Collections.Generic.IEnumerable<string>'
Essentially, I am trying to save choices from a checkboxlist (one or more selections) as a comma-separated list in the DB.
And this doesn't even get me over the problem of how do I use the enum. Any thoughts?
For some background:
I understand that the appropriate way to handle this relationship would be to create a separate table and use IList<MyEnum>. However, this is a simple list that I do not intend to manipulate with edits (in fact, no driver is used in this scenario as I handle this on the front-end with a controller and routes). I am just capturing data and redisplaying it in the Admin view for statistical/historical purposes. I may even consider getting rid of the Part (considering the following post: Bertrand's Blog Post.
It should be:
part.MyProperty = new[] {"foo", "bar"};
for example. The part's setter will store the value on the record's property as a comma-separated string, which will get persisted into the DB.
If you want to use enum values, you should use the Parse and ToString APIs that .NET provide on Enum.

Resources