I have below schema definition to represent commission amount in my openapi contract.
commissionAmount:
type: number
minimum: -99999.99
maximum: 99999.99
Generated Code:
#Valid
#DecimalMin("-99999.99") #DecimalMax("99999.99")
public BigDecimal getCommissionAmount() {
return commAmt;
}
The generated code is good and as expected. I just wanted to know are these -99999.99 and 99999.99 valid values for minimum and maximum.
The reason for asking this question is it does not check the limit in the fractional part. For example, I expect 12345.678 is invalid , 12345.67 is valid. But it marks both as valid.
I read #Digits is used to check for the digit limit of integer and fractional part. How do I tell openapi-generator-maven-plugin to annotate Digits as well?
Expected Generated Code:
#Valid
#Digits(integer = 5, fraction = 2)
#DecimalMin("-99999.99") #DecimalMax("99999.99")
public BigDecimal getCommissionAmount() {
return commAmt;
}
The way to specify this in OpenAPI would be using multipleOf:
commissionAmount:
type: number
minimum: -99999.99
maximum: 99999.99
multipleOf: 0.01
However, using the OpenAPI Generator will not produce an annotation for this. The reason being is that there is no javax.validation annotation that can represent multipleOf effectively (imagine trying to express multipleOf: 0.02- #Digits would be insufficient).
However, you can create your own annotation as this user has: https://github.com/OpenAPITools/openapi-generator/issues/2192#issuecomment-575132233
With the following annotation and validator:
#Target({METHOD, FIELD})
#Retention(RUNTIME)
#Repeatable(MultipleOf.List.class)
#Constraint(validatedBy = MultipleOfValidator.class)
public #interface MultipleOf {
double value();
String message() default "{error.multipleOf}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default { };
#Target({ METHOD, FIELD })
#Retention(RUNTIME)
#Documented
#interface List {
MultipleOf[] value();
}
}
public class MultipleOfValidator implements ConstraintValidator<MultipleOf, Number> {
private double value;
#Override
public void initialize(MultipleOf constraintAnnotation) {
this.value = constraintAnnotation.value();
}
#Override
public boolean isValid(Number value, ConstraintValidatorContext context) {
return (value == null) || (value.doubleValue() / this.value) % 1 == 0;
}
}
You would then be able to fork the generator and add your new annotation to the template: https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/Java/beanValidationCore.mustache
With a line like this:
{{#multipleOf}}#MultipleOf({{multipleOf}}){{/multipleOf}}
Related
I want to check for zip i.e. if zip should have 5 digits exactly like 12346 and also it shouldn't have repetitive consecutive numbers like 00000, 11111 ,22222 .... 99999 what will be the regex to check for this validation ? I need two regular expressions in this case combining one positive and one negative which i.e. to find number which should have exact 5 digit and should not have repetitive numbers like 11111 , 22222 etc..
So I was able to do this with custom ConstraintValidator as follows:
public class ZipCodeValidator implements ConstraintValidator<ValidateZip, String> {
#Override
public boolean isValid(String zip, ConstraintValidatorContext context) {
return zip.matches("(^[\\d]{5}$){1}") && !zip.equals(APPConstants.ZIP_ALL_ZEROES);
}
}
and with annotation:
#Constraint(validatedBy = ZipCodeValidator.class)
#Target({ ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ValidateZip {
String message() default "Resident Zip Code not valid from request, unable to process.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
then using this annotation on field where validation is required:
#ValidateZip
public String residentPostalcode;
I have got a spring boot server and would like to validate my values by spring. Using the #Size validation I can set the max size. But i would like to get this max size from my application.property file.
I have already tried to load this value by "#Value(...)" but I can not use this value in the "#Size" field.
#Value("${max.size.in.properties}")
private int MAX_SIZE;
#Size(max = 10)
private String description;
We can programmatically specify constraints using Hibernate Validator, which is already available in the classpath when using spring-boot-starter-web.
Given:
class MyObject {
private String description;
...
}
We can setup constraints like this:
#Value("${max.size.in.properties}")
private int MAX_SIZE;
HibernateValidatorConfiguration configuration = Validation
.byProvider( HibernateValidator.class )
.configure();
ConstraintMapping constraintMapping = configuration.createConstraintMapping();
constraintMapping.type( MyObject.class )
.property( "description", FIELD )
.constraint( new SizeDef().min( 1 ).max( MAX_SIZE ) );
and validate an instance of the object with:
Validator validator = configuration.addMapping( constraintMapping )
.buildValidatorFactory()
.getValidator();
Set<ConstraintViolation<MyObject>> constraintViolations =
validator.validate( myObjectInstance );
if (constraintViolations.size() > 0) {
... // handle constraint violations
}
The bad news: there's no way to do what you want with standard annotations from Java Validation API.
The good news: you can easily create a custom annotation that does exactly what you want.
You need to create a custom validation annotation (let's call it #ConfigurableSize) that takes as parameters two strings, one for the name of the property holding the min size and one for the name of the property holding the max size.
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Repeatable(ConfigurableSize.List.class)
#Constraint(validatedBy = {ConfigurableSizeCharSequenceValidator.class})
public #interface ConfigurableSize {
String message() default "size is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String minProperty() default "";
String maxProperty() default "";
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Documented
#interface List {
ConfigurableSize[] value();
}
}
The validator will retrieve the property values upon initialization, then it will perform the exact same validation checks as the #Size constraint. Even the constraint violation will have the exact same message. Please notice that if the property name is omitted the min and max will default respectively to 0 and Integer.MAX_VALUE, i.e. the same defaults for #Size.
public class ConfigurableSizeCharSequenceValidator implements ConstraintValidator<ConfigurableSize, CharSequence> {
private final PropertyResolver propertyResolver;
private int min;
private int max;
#Autowired
public ConfigurableSizeCharSequenceValidator(PropertyResolver propertyResolver) {
this.propertyResolver = propertyResolver;
}
#Override
public void initialize(ConfigurableSize configurableSize) {
String minProperty = configurableSize.minProperty();
String maxProperty = configurableSize.maxProperty();
this.min = "".equals(minProperty) ? 0 :
propertyResolver.getRequiredProperty(minProperty, Integer.class);
this.max = "".equals(maxProperty) ? Integer.MAX_VALUE :
propertyResolver.getRequiredProperty(maxProperty, Integer.class);
validateParameters();
}
private void validateParameters() {
if (this.min < 0) {
throw new IllegalArgumentException("The min parameter cannot be negative.");
} else if (this.max < 0) {
throw new IllegalArgumentException("The max parameter cannot be negative.");
} else if (this.max < this.min) {
throw new IllegalArgumentException("The length cannot be negative.");
}
}
#Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
} else {
int length = value.length();
boolean retVal = length >= this.min && length <= this.max;
if (!retVal) {
HibernateConstraintValidatorContext hibernateContext =
context.unwrap(HibernateConstraintValidatorContext.class);
hibernateContext.addMessageParameter("min", this.min)
.addMessageParameter("max", this.max);
hibernateContext.disableDefaultConstraintViolation();
hibernateContext
.buildConstraintViolationWithTemplate("{javax.validation.constraints.Size.message}")
.addConstraintViolation();
}
return retVal;
}
}
}
You apply the custom annotation in your bean
public class SomeBean {
#ConfigurableSize(maxProperty = "max.size.in.properties")
private String description;
}
Then finally in your application.properties you'll define the property
max.size.in.properties=10
And that's it. You can find more details and a full example in this blog post:
https://codemadeclear.com/index.php/2021/03/22/easily-configure-validators-via-properties-in-a-spring-boot-project/
you can do it like this post per java reflection https://www.baeldung.com/java-reflection-change-annotation-params
This is not possible as annotations require constant expressions (static final) and #Value cannot be used to inject values into static final fields.
Maybe this project might help you out: https://github.com/jirutka/validator-spring.
It allows you to use Spring Expression Language together with bean validation.
I came across this method signature in Spring Component interface.
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Indexed
public #interface Component
{
String value() default "";
}
What is the method signature String value() default ""; means?
How and When should we define such syntax for our coding purposes?
This is no method signature. it means that you can pass a String as parameter to the Component annotation, like this:
#Component(value = "value")
If you don't specify a value your self, the default value "" will be used.
If it had been like this:
String value(); // without the default
value would have been a mandatory parameter. Trying to use Component like this:
#Component()
would lead to an Exception, since you didn't provide a value.
EDIT: when to use.
If you don't know much about this syntax, or annotations in general, you shouldn't use them. About everything that can be done using annotations, especially custom made ones, can also be done without annotations.
Let's say you want to create an annotation to validate the value of a field.
I'll be using the example of Belgian postal codes. They all are 4 digits, and are between 1000 and 9999.
#Target( {ElementType.FIELD})
#Retention( RetentionPolicy.RUNTIME)
#Constraint( validatedBy = ValidatePostalCodeImpl.class)
public #interface ValidatePostalCode{
String message() default "You have entered an invalid postal code";
Class<?>[] groups() default {}; // needed for the validation
Class<? extends Payload>[] payload() default{}; // needed for the validation
int maxValue() default 9999; // so, by default, this will validate based
int minValue() default 1000; // on these values, but you will be able to
// override these
}
/* Validation implementation */
public class ValidatePostalCodeImpl implements ConstraintValidator<ValidatePostalCode, Integer> {
int upperValue;
int lowerValue;
#Override
public void initialize(ValidatePostalCode validate) {
this.upperValue = validate.maxValue(); // here you call them as if they indeed were regular methods
this.lowerValue = validate.minValue();
}
#Override
public boolean isValid(Integer integer, ConstraintValidatorContext context) {
return integer >= lowerValue && integer <= upperValue;
}
}
/* Usage */
#Entity
#Table(name = "addresses")
public class Addresses {
// home address -> In Belgium, so has to be between the default values:
#ValidatePostalCode
Integer belgianPostalCode;
// vacation home in another country, let's say the PC's there are between
// 12000 and 50000
#ValidatePostalCode(minValue = 12000, maxValue = 50000)
Integer foreignPostalCode;
}
Sure, this is a very limited example, but it should get you an idea.
The #interface keyword is used to define an annotation. This annotation has a property called value, which you could specify explicitly:
#Component(value = "myValue")
Or in the shorthand form:
#Component("myValue")
If you don't specify the value, it will default to "", as defined by the default keyword.
I have a Jersey Rest API like this:
#POST
#Path("/doorder")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("text/plain")
public String doOrder(#BeanParam final #Valid OrderBean order) {
// Some implementation here
}
All my inputs are store in this bean:
#AddressAtLeastOne
public final class OrderBean {
#FormDataParam("address")
private String address;
#FormDataParam("city")
private String city;
#FormDataParam("postcode")
private String postcode;
// Other member variables
// Getters and setters
}
I added an annotation to validate the address (#AddressAtLeastOne). The address is valid if at least one of the 3 fields has a value.
Here's the annotation definition:
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
#Retention(RUNTIME)
#Constraint(validatedBy = AddressAtLeastOneValidator.class)
#Documented
public #interface AddressAtLeastOne {
String message() default "Address requires at least one field";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And here's the validator:
public class AddressAtLeastOneValidator implements ConstraintValidator<AddressAllOrNone, OrderBean> {
#Override
public boolean isValid(OrderBean demoBean, ConstraintValidatorContext constraintValidatorContext) {
// Check for at least one value
if((demoBean.getAddress() != null && !demoBean.getAddress().equals("") ||
(demoBean.getCity() != null && !demoBean.getCity().equals("")) ||
(demoBean.getPostcode() != null && !demoBean.getPostcode().equals("")))) {
return true;
}
return false;
}
}
Everything is fine! But now I want to rename the annotation #AddressAtLeastOne to #AtLeastOne and make it generic, so that I can apply it to any class. I need a mechanism where I can specify which member variables are part of the group I want to validate with #AtLeastOne. How can I do that?
One approach of doing this is to use Reflection -
Create a custom annotation suppose #GroupNotNullField and apply this annotation to all fields in bean class in which at least one field should have value. By this way, you can skip some fields in which validation is not required.
In the validator class, get all the fields of the bean class using Reflection
Check all the fields which are annotated with #GroupNotNullField annotation
Get the value of all such fields and check that at least one has value.
Return true or false depending on validation check.
I am using Spring Boot and javax validation, particularly #Size.
I am trying to grab the values for size constraints from the application.properties file:
#Size(min= #Value("${device.name.minsize}"), max=#Value("${device.name.maxsize}"))
private String name;
But I receive the following compile time error:
Error:(26, 16) java: annotation not valid for an element of type int
Trying to fix this issue I'm attempting the following:
#Size(min=Integer.parseInt( #Value("${device.name.minsize}") ), max=Integer.parseInt( #Value("${device.name.maxsize}") ) )
But this has multiple errors as well.
How can I convert the #Value annotations correctly?
Am I headed down the wrong path?
What I am looking for is a clean way to pull size limitations out of code and into configuration that I can access server side and in my templated angularJS/html.
I don't think you'll be able to do that. Annotations require constant values as their parameters, since they need to be handled at compile time.
You could externalize the xml:
http://beanvalidation.org/1.1/spec/#xml-config
Alternatively, if you just want to use JSR-303 annotation metadata in AngularJS, you might have a look at Valdr and Valdr BeanValidation:
https://github.com/netceteragroup/valdr
https://github.com/netceteragroup/valdr-bean-validation
For yet another approach take a look at https://github.com/jirutka/validator-spring.
It allows you to use SpSEL expressions in bean validation annotations including config properties.
You won't be able to use the standard annotations like #Size though, you'd have to formulate the constraints as SpEL expressions.
The bad news: there's no way to do what you want with standard annotations from Java Validation API.
The good news: you can easily create a custom annotation that does exactly what you want.
You need to create a custom validation annotation (let's call it #ConfigurableSize) that takes as parameters two strings, one for the name of the property holding the min size and one for the name of the property holding the max size.
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Repeatable(ConfigurableSize.List.class)
#Constraint(validatedBy = {ConfigurableSizeCharSequenceValidator.class})
public #interface ConfigurableSize {
String message() default "size is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String minProperty() default "";
String maxProperty() default "";
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Documented
#interface List {
ConfigurableSize[] value();
}
}
The validator will retrieve the property values upon initialization, then it will perform the exact same validation checks as the #Size constraint. Even the constraint violation will have the exact same message. Please notice that if the property name is omitted the min and max will default respectively to 0 and Integer.MAX_VALUE, i.e. the same defaults for #Size.
public class ConfigurableSizeCharSequenceValidator implements ConstraintValidator<ConfigurableSize, CharSequence> {
private final PropertyResolver propertyResolver;
private int min;
private int max;
#Autowired
public ConfigurableSizeCharSequenceValidator(PropertyResolver propertyResolver) {
this.propertyResolver = propertyResolver;
}
#Override
public void initialize(ConfigurableSize configurableSize) {
String minProperty = configurableSize.minProperty();
String maxProperty = configurableSize.maxProperty();
this.min = "".equals(minProperty) ? 0 :
propertyResolver.getRequiredProperty(minProperty, Integer.class);
this.max = "".equals(maxProperty) ? Integer.MAX_VALUE :
propertyResolver.getRequiredProperty(maxProperty, Integer.class);
validateParameters();
}
private void validateParameters() {
if (this.min < 0) {
throw new IllegalArgumentException("The min parameter cannot be negative.");
} else if (this.max < 0) {
throw new IllegalArgumentException("The max parameter cannot be negative.");
} else if (this.max < this.min) {
throw new IllegalArgumentException("The length cannot be negative.");
}
}
#Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
} else {
int length = value.length();
boolean retVal = length >= this.min && length <= this.max;
if (!retVal) {
HibernateConstraintValidatorContext hibernateContext =
context.unwrap(HibernateConstraintValidatorContext.class);
hibernateContext.addMessageParameter("min", this.min)
.addMessageParameter("max", this.max);
hibernateContext.disableDefaultConstraintViolation();
hibernateContext
.buildConstraintViolationWithTemplate("{javax.validation.constraints.Size.message}")
.addConstraintViolation();
}
return retVal;
}
}
}
You apply the custom annotation in your bean
public class Device {
#ConfigurableSize(minProperty = "device.name.minsize", maxProperty = "device.name.maxsize")
private String name;
}
Then finally in your application.properties you'll define the properties
device.name.minsize=4
device.name.maxsize=8
And that's it. You can find more details and a full example in this blog post:
https://codemadeclear.com/index.php/2021/03/22/easily-configure-validators-via-properties-in-a-spring-boot-project/