How can i test a DTO validation of 2 fields #interface - java

I am a java beginner and I need help with testing this:
what I am trying to do: I need at least one of the fields with value to accept it.
How should test my implementation?
My classes:
#Constraint(validatedBy = BankOrBarPresenceValidador.class)
#Target({ TYPE, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Documented
public #interface VerifyBankOrBarPresence {
String message() default "{constraints.VerifyBankOrBarPresence.message}";
Class <?> [] groups() default {};
Class <? extends Payload> [] payload() default {};
String barCodeInvoice();
String bankDestinationNumber();
}
Implementation:
public class BankOrBarPresenceValidador implements ConstraintValidator<VerifyBankOrBarPresence, Object> {
private String barCodeInvoice;
private String bankDestinationNumber;
#Override
public void initialize(VerifyBankOrBarPresence constraintAnnotation) {
barCodeInvoice = constraintAnnotation.barCodeInvoice();
bankDestinationNumber = constraintAnnotation.bankDestinationNumber();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
try{
boolean barcodeIsPresent = barCodeInvoice.length() > 0;
boolean bankDestinationNumberIsPresent = bankDestinationNumber.length() > 0;
return barcodeIsPresent || bankDestinationNumberIsPresent;
}
catch (final Exception e) {
throw new BusinessException("Failed validating if bar code or bank destination number are present", e);
}
}
}
MY DTO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#VerifyBankOrBarPresence(barCodeInvoice = "barCodeInvoice", bankDestinationNumber="bankDestinationNumber", message = "Bar Code Invoice or bank Destination Number code not be null")
public class VoucherDTO implements Serializable {
private String barCodeInvoice;
private String bankDestinationNumber;
}

Related

How to apply a custom annotation to a List<String>?

I created an annotation in order to validate the value of a String field (like an enum) :
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = GenericStringSetValidator.class)
public #interface ValidStringValue {
String message() default "Must be a valid value. Found: ${validatedValue}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] values() default {};
}
And the GenericStringSetValidator used by the annotation :
public class GenericStringSetValidator implements ConstraintValidator<ValidStringValue, String> {
private Set<String> validValues;
#Override
public void initialize(ValidStringValue constraint) {
validValues = Arrays.stream(constraint.values())
.collect(toSet());
}
#Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) return false;
return validValues.contains(value);
}
}
I already use this in the following class whithout any trouble :
public class SampleClass{
private String id;
#ValidStringValue(values = {"IN_PROGRESS", "COMPLETED", "DELETED"})
private String status;
}
But I also want to use it in an other class with List<String> in order to validate each items of the List. Is it possible to configure my ValidStringValue interface to make it usable on a String and also a List<String> ?
public class SampleClassWithList{
private String id;
#ValidStringValue(values = {"A", "B", "C"})
private List<String> sampleList;
}
Thanks a lot for your help :)

How to add multiple constraint violations for the same property node?

I have a custom ConstraintValidator. What i want to do, is to add multiple constraint violations for the same property node, with the same message, and different dynamic payload. Is this possible?
Custom annotation:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = CustomFieldConstraintValidator.class)
public #interface CustomFieldConstraint {
String message() default "message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Custom constraint validator:
public class CustomFieldConstraintValidator implements ConstraintValidator<CustomFieldConstraint, Integer> {
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
var valid = true;
var hibernateConstraintValidatorContext = context.unwrap(HibernateConstraintValidatorContext.class);
hibernateConstraintValidatorContext.disableDefaultConstraintViolation();
if (value > 0) {
valid = false;
hibernateConstraintValidatorContext.withDynamicPayload(Map.of("must_be_less_than", 0))
.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addPropertyNode("field")
.addConstraintViolation();
}
if (value > 5) {
valid = false;
hibernateConstraintValidatorContext.withDynamicPayload(Map.of("must_be_less_than", 5))
.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addPropertyNode("field")
.addConstraintViolation();
}
return valid;
}
}
Validator test:
public class HibernateValidatorTest {
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
#Test
void multipleFailuresForSameField() {
var foo = new Foo(10);
var constraintViolations = validator.validate(foo);
assertEquals(2, constraintViolations.size());
}
public static class Foo {
#CustomFieldConstraint
private int field;
public Foo(int field) {
this.field = field;
}
}
}
The test is failing with expected: <2> but was: <1>.
You have this issue because ConstraintViolation are returned as a set and, by design, the dynamic payload is not included in the equals() method. See https://github.com/hibernate/hibernate-validator/blob/master/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java#L251 .
Thus your 2 constraints are considered equal at the Set level.
You need to change the way you do things and enrich the payload with both information instead.

#NotNull Annotation does not work on Double

I am trying to validate Double in the request of my dropwizard application. Below is the code for same
import javax.validation.constraints.NotNull;
#PATH("/path")
public class MyResource {
#GET
public Response addNumber( #QueryParam("first") #NotNull Double first, #QueryParam("second") #NotNull Double second) {
return Response.ok("added").build();
}
}
I get the below error when querying the resource localhost/path?first=46.76&second=67.56
{
"errors": [
"query param first may not be null",
"query param second may not be null"
]
}
If we remove the #NotNull from the resource the resource responds successfully.
Why does #NotNull say it should be not null although the values are present?
I had the same requirement. So implemented custom validator.
#Retention(RUNTIME)
#Target({ElementType.FIELD})
#Constraint(validatedBy = NotZeroDoubleValidatorImpl.class)
public #interface NotZeroDoubleValidator {
String message() default "{Double value is required}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Field to validate against 0.
*/
double field() default 0;
}
And
public class NotZeroDoubleValidatorImpl implements ConstraintValidator<NotZeroDoubleValidator, Double> {
#Override
public void initialize(final NotZeroDoubleValidator notZeroDoubleValidator) {
}
#Override
public boolean isValid(final Double value, final ConstraintValidatorContext context) {
return Double.compare(value, 0) != 0;
}
}

Not able to validate data from enum using hibernate validator

I am using hibernate validator for validating DTO object. Here facing one chalange not able check data is available or not in Enum
I have used below enum
public enum DeliveryMethodEnum {
IMMEDIATE(1),
SCHEDULED(2);
private Integer deliveryMethod;
private DeliveryMethodEnum(Integer deliveryMethod) {
this.deliveryMethod = deliveryMethod;
}
public Integer getDeliveryMethod() {
return deliveryMethod;
}
}
In DTO use custom annotation as below
#EnumValidator(message = "1008", enumClass =
DeliveryMethodEnum.class) private Integer deliveryMethod;
Request is below
{
"deliveryMethod": 8
}
It should show invalid request but its not valided from enum.
I have below custome annotation code
#Documented
#Constraint(validatedBy = EnumValidatorImpl.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#NotNull(message = "Value cannot be null")
#ReportAsSingleViolation
public #interface EnumValidator {
Class<? extends Enum<?>> enumClass();
String message() default "Value is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, Integer> {
List<String> valueList = null;
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return !valueList.contains(String.valueOf(value));
}
#Override
public void initialize(EnumValidator constraintAnnotation) {
valueList = new ArrayList<>();
Class<? extends Enum<?>> enumClass = constraintAnnotation.enumClass();
#SuppressWarnings("rawtypes")
Enum[] enumValArr = enumClass.getEnumConstants();
for (#SuppressWarnings("rawtypes")
Enum enumVal : enumValArr) {
valueList.add(enumVal.toString().toUpperCase());
}
}
}
This is because there is error in your EnumValidatorImpl class.
Following line
!valueList.contains(String.valueOf(value));
Actual value of valueList: [IMMEDIATE, SCHEDULED]
and value: 8 from body payload {"deliveryMethod": 8}
So validation will fail always.
Also !valueList.contains this will be valid for non-enum value,
if you need to validate for only enum values then remove ! symbol.
Solutions
1. Accept as String from request as
{
"deliveryMethod": "IMMEDIATE"
}
or
{
"deliveryMethod": "SCHEDULED"
}
Hence need to updateDTO, Now value is one of the enum/not and gets validated
Create valueList as [1, 2] and validate.
Here is updated EnumValidatorImpl.java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, Integer> {
List<Integer> valueList = null;
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return valueList.contains(value);
}
#Override
public void initialize(EnumValidator constraintAnnotation) {
valueList = new ArrayList<>();
Class<? extends Enum<?>> enumClass = constraintAnnotation.enumClass();
Enum[] enumValArr = enumClass.getEnumConstants();
valueList = Arrays.stream(enumValArr)
.map(anEnum -> ((DeliveryMethodEnum) anEnum).deliveryMethod)
.collect(Collectors.toList()); // This will make problem, since it will only be used for DeliveryMethodEnum enum type and not for any other enum, if it is not the problem then this can be done,else I could not think of any other way
}
}

Custom validator of a bean field using other field values

Not sure if this possible. Wondering if I can create a custom constraint for a bean's field where it would use values of other field of the same class object. I tried implementing this with the code below:
public final class AWRRecordObject extends DoaAbstract implements
java.io.Serializable {
private static final long serialVersionUID = 1L;
#ValidateApplicationType
private String applicationType;
private String recordData;
private String programSource;
// ...
}
public class ApplicationTypeValidator implements
ConstraintValidator<ValidateApplicationType, AWRRecordObject> {
private Class<AWRRecordObject> awrRecObj; // ?
#Override
public void initialize(ValidateApplicationType constraintAnnotation) {
this.awrRecObj = constraintAnnotation.value();
}
#Override
public boolean isValid(String arg0, ConstraintValidatorContext arg1) {
// verify if the application type is A1 and length is 512
if ((awrRecObj.getApplicationType()
.equalsIgnoreCase(AWRConstant.AWR_W2))
&& (awrRecObj.getRecordData().toString().length() != AWRConstant.W2_RECORD_LEN)
&& (awrRecObj.getProgramSource()
.equalsIgnoreCase(AWRConstant.SOURCE_BATCH))) {
// create AWRRequestError Object and add it to AWRRecordObject
AWRUtility.createAWRRequestErrorObject(awrRecObj,
AWRErrorTypeCode.W2_RECORD_LENGTH_ERROR,
awrRecObj.getRecordData());
return false;
// ...
}
return true;
}
}
#Target({ METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = { ApplicationTypeValidator.class })
public #interface ValidateApplicationType {
String message() default "{com.myproject.validation.ApplicationType.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<AWRRecordObject> value();
}
The code reads a fixed length file and stores parsed values into a bean:
public static AWRRecordObject bufferedReader() throws IOException {
int lineCount = 0;
long time1 = System.currentTimeMillis();
File file = new File("D://TestData/test.txt");
BufferedReader br = new BufferedReader(new FileReader(file), 8192);
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
String line;
while ((line = br.readLine()) != null) {
String type = line.length() > 2 ? line.substring(0, 2) : line;
AWRRecordObject record = new AWRRecordObject(line, type, lineCount);
Set<ConstraintViolation<AWRRecordObject>> constraintViolations = validator
.validate(record);
if (constraintViolations.size() > 0) {
Iterator it = constraintViolations.iterator();
while (it.hasNext()) {
ConstraintViolation<AWRRecordObject> cv = (ConstraintViolation<AWRRecordObject>) it
.next();
System.out.println(cv.getInvalidValue());
System.out.println(cv.getMessage());
}
}
return record;
}
}
Unfortunately, I am getting the following error message:
Exception in thread "main" javax.validation.UnexpectedTypeException: No validator could be found for type: java.lang.String
at org.hibernate.validator.engine.ConstraintTree.verifyResolveWasUnique(ConstraintTree.java:383)
Looks like there are two things you need to do here:
1 . You currently have your custom validation being applied to a field of your target bean. You need to move it to the class instead.
#ValidateApplicationType
public final class AWRRecordObject
2 . Your annotation definition needs to be changed, to allow the annotation to be applied to classes.
#Target({ TYPE, ANNOTATION_TYPE })

Categories

Resources