Validator error ordering - validation

What I want to achieve is ordered error messages. Give the following bean class
public class DummyBean{
#NotNull
public String firstName;
#NotNull
public String lastName;
}
And using LocalValidatorFactoryBean like this:
private Validator validator;
#Autowired
public DummyController(Validator validator) {
this.validator = validator;
}
public void validate(DummyBean bean){
Map<String, String> failureMessages = new LinkedHashMap<String, String>();
for (ConstraintViolation<DummyBean> failure : validator.validate(bean)) {
failureMessages.put(failure.getPropertyPath().toString(), failure.getMessage());
}
}
How can I make sure that errors messages related to property "firstName" always come first related to "lastName"?
Afaik only group sequences can be ordered but I'm hoping that maybe someone came across this problem already

You are right, if you want to enforce an order you need to use a group sequence. However, in this case validation will stop as soon as a constraint violation occurs. This means in this case you would never get constraint violation for both firstName and lastName.
Also consider that you are getting a Set of violations and that there is no guaranteed iteration order over a set.

Related

EqualsVerifier in SpringBoot

I am creating the tests for two classes that share a list of data. When I use EqualsVerifier I get an error because it is asking me for a list with data shared by these two classes.
This is the error:
Recursive datastructure. Add prefab values for one of the following types: CustomerView, List<YearConfigView>, YearConfigView
This is the #Test class:
#Test
public void CustomerViewTest() {
EqualsVerifier.forClass(CustomerView.class).withRedefinedSuperclass().withGenericPrefabValues(CustomerView.class).verify();
}
#Test
public void YearConfigViewTest() {
EqualsVerifier.forClass(YearConfigView.class).suppress(Warning.ALL_FIELDS_SHOULD_BE_USED).verify();
}
CustomerView.java:
public class CustomerView extends EntityBase<Integer> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private List<YearConfigView> yearConfigs;
#JsonProperty("current_year_config")
public YearConfigView getCurrentYearConfig() {
if (this.getYearConfigs() == null || this.getYearConfigs().isEmpty()) {
return null;
}
int currentYear = LocalDate.now().getYear();
return this.yearConfigs.parallelStream().filter(yc -> yc.getYear() == currentYear).findAny().orElse(null);
}
}
YearConfigView.java:
public class YearConfigView extends EntityBase<Integer> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private CustomerView customer;
private Integer year;
private String comments;
}
My problem: What do I have to change or add in EqualsVerifier to solve the problem?
Creator of EqualsVerifier here.
Without seeing your classes (I'd like to see exactly which fields CustomerView and YearConfigView have, and how equals and hashCode are implemented on both classes), it's hard to say for certain what's going on, but I suspect it's this:
CustomerView has a reference to YearConfigView (or perhaps List<YearConfigView>), and YearConfigView has a reference to CustomerView.
EqualsVerifier, while doing its thing, tries to make instances of the classes it's verifying, and giving its fields proper values too. In order to do that, it must recursively instantiate the class's fields and give those values too. Usually, that's not a problem, but sometimes you run into a loop, like in your case: in order to create a value for CustomerView, it must have a value for YearConfigView and vice versa.
The way to avoid this, is by giving EqualsVerifier some 'prefab values'. I see you've already tried to do something like this, by adding .withGenericPrefabValues(CustomerView.class). (This method requires 2 parameters so I suspect you may have removed some code before posting it to StackOverflow 😉) This only works if CustomerView is itself a generic class, which I can't verify because you didn't post that particular piece of code. In any event, you shouldn't give generic prefab values or regular prefab values for the class you're testing.
In general, though, your tests should both give a prefab value for the other class. That would look like this:
#Test
public void CustomerViewTest() {
YearConfigView one = new YearConfigView();
one.setYear(2020);
YearConfigView two = new YearConfigView();
two.setYear(2021);
EqualsVerifier.forClass(CustomerView.class)
.withRedefinedSuperclass()
.withPrefabValues(YearConfigView.class, one, two)
.verify();
}
#Test
public void YearConfigViewTest() {
CustomerView one = new CustomerView();
one.setName("Alice");
CustomerView two = new CustomerView();
two.setName("Bob");
EqualsVerifier.forClass(YearConfigView.class)
.suppress(Warning.ALL_FIELDS_SHOULD_BE_USED)
.withPrefabValues(CustomerView.class, one, two)
.verify();
}
Note that I still don't know which fields are included in your equals methods, so I'm only making an educated guess about how to instantiate your classes.
For more information, see the relevant page in the EqualsVerifier documentation. Since the classes are JPA entities, this page might also be helpful: it explains how the #Id is treated by EqualsVerifier.

Throw error when properties marked with #JsonIgnore are passed

I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.

Jackson ObjectMapper ignores JsonInclude.Include.NON_NULL

I have a project in Spring Boot 1.5.2. For some reason, I haven't been able to make the ObjectMapper ignore null fields during serialization. Here's the setup:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class CustomerInfo{
private Long customerId;
private String fullName;
//some more fields
//getters and setters
}
#Service
public class ObjectMapperTester{
#Autowired
private ObjectMapper objectMapper;
public void test(){
CustomerInfo ci = new CustomerInfo;
ci.setFullName("Foo");
objectMapper.writeValueAsString(ci);
//I get JsonMappingException here
}
}
Up until now, I've always used #JsonInclude(JsonInclude.Include.NON_NULL) annotation with my classes to ignore null fields. That works perfectly well when I return any kind of object in RestController methods, so I don't see any null fields in the response output. But this one resists working. I get an exception when trying to writeValueAsString when the customerId field is null. The ObjectMapper is trying to get customerId value through getter, which in turn does the following:
//In fact compiler transforms the getter to be so, otherwise I just return the customerId.
return this.customerId.longValue();
And that of course throws NullPointerException.
I've tried instructing the mapper manually to ignore null fields during serialization this way:
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
But that doesn't help either. Any other ideas?
UPDATE
I've sort of figured out the problem. In fact, the type of the customerId field used to be the primitive long. Then I changed it to Long without using the IDE refactoring, which left the getter like this:
public long getCustomerId(){
return customerId;
}
And the compiler converted it to
public long getCustomerId(){
return customerId.longValue();
}
After fixing the getter return type to Long, the problem got solved and I indeed don't get the customerId field in the output. But the getCustomerId() method is still invoked. That's why I said sort of. Why does the mapper need to invoke the getter if it's going to ignore it? I've tried removing the class-wide annotation and added it to the fields. But still, the getter method gets invoked.

preprocess configuration values in Spring

In a Spring bean, I need to process a configuration property before using is, e.g.:
#Component
class UsersController {
#Value("${roles}")
private String rolesAsString;
private List<String> roles;
#PostConstruct
public void initRoles() {
// just an example, not necessarily string splitting
roles = rolesAsString.split(",");
}
This works, but I am left with an unneeded member variable 'rolesString'. What would be a clean concise way to only keep the processed value?
Properties is
roles=role1,role2,role3
Code is :
#Value("#{'${roles}'.split(',')}")
private List<String> roles;

Custom bean validation not working

In my Student class I have many fields which I am storing in the database and I also have one field to store photo( for that I am using MultiPartFile datatype) and I am validating this field using custom validation.
Below is code for validation
#Component
public class PhotoValidator implements Validator{
#Override
public boolean supports(Class<?> clazz) {
return Student.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Student student=(Student)target;
if(student.getStudentPhoto()!=null){
if(student.getStudentPhoto().getSize()==0){
errors.rejectValue("file", "missing.file");
}
}
if(!student.getStudentPhoto().getOriginalFilename().endsWith(".jpg")){
errors.rejectValue("file", "invalid.file");
}
}
}
In the controller I am implementing it like this
#InitBinder
protected void initBinderStudent(WebDataBinder binder) { binder.setValidator(photoValidator);
}
My Student Model is :-
#Entity
#Table(name = "STUDENT")
public class Student extends UrlEntity {
#Transient
MultipartFile studentPhoto;
#Column(name = "COURSE_TYPE", nullable = false)
#NotNull(message = "Course Type: Course Type can not be left blank")
private in.jmi.constants.CourseType courseType;
#OneToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "STUDENT_USER")
#Valid
private User user;
This custom validation of photo is not working and it also mess up the other annotation based validation that I am having here.
I have checked many posts in stackoverflow but couldn't find any relation to this particular problem.
Note:-If I remove the validation code from controller the code works just fine doing all the validations it is supposed to do.
You are mixing approaches in your example. You are not showing the imports in your code example, but the PhotoValidator class does not implement a Bean Validation constraints. It might be some Spring/JSF specific validator!?
To implement a Bean Validation constraint, you need to define a constraint annotation and at least one implementing ConstraintValidator. This is all described in Creating custom constraints. There are plenty of examples out there how to write a custom constraint.

Resources