#1557 - Add support for @DecimalMin and @DecimalMax.

Add support for decimal-based type validation constraints.
This commit is contained in:
Greg L. Turnquist
2021-06-29 16:30:05 -05:00
committed by Oliver Drotbohm
parent 08bc96493e
commit 33e853cbb5
5 changed files with 65 additions and 14 deletions

View File

@@ -342,9 +342,9 @@ Each property will get the following attributes defined:
|`readOnly`| Set to `true` if there's no setter method for the property. If that is present, use Jackson's `@JsonProperty(Access.READ_ONLY)` on the accessors or field explicitly. Not rendered by default, thus defaulting to `false`.
|`regex`| Can be customized by using JSR-303's `@Pattern` annotation either on the field or a type. In case of the latter the pattern will be used for every property declared as that particular type. Not rendered by default.
|`required`| Can be customized by using JSR-303's `@NotNull`. Not rendered by default and thus defaulting to `false`. Templates using `PATCH` as method will automatically have all properties set to not required.
|`max`| The maximum value allowed for the property. Derived from Hibernate Validator's `@Range` or JSR-303's `@Max` annotations.
|`max`| The maximum value allowed for the property. Derived from Hibernate Validator's `@Range` or JSR-303's `@Max` and `@DecimalMax` annotations.
|`maxLength`| The maximum length value allowed for the property. Derived from Hibernate Validator's `@Length` annotation.
|`min`| The minimum value allowed for the property. Derived from Hibernate Validator's `@Range` or JSR-303's `@Min` annotations.
|`min`| The minimum value allowed for the property. Derived from Hibernate Validator's `@Range` or JSR-303's `@Min` and `@DecimalMin` annotations.
|`minLength`| The minimum length value allowed for the property. Derived from Hibernate Validator's `@Length` annotation.
|`options`| The options to select a value from when submitting the form. For details, see <<mediatypes.hal-forms.options>>.
|`prompt`| The user readable prompt to use when rendering the form input. For details, see <<mediatypes.hal-forms.i18n.prompts>>.

View File

@@ -468,7 +468,7 @@ public abstract class AffordanceModel {
* @since 1.3
*/
@Nullable
default Long getMin() {
default Number getMin() {
return null;
}
@@ -479,7 +479,7 @@ public abstract class AffordanceModel {
* @since 1.3
*/
@Nullable
default Long getMax() {
default Number getMax() {
return null;
}

View File

@@ -20,11 +20,14 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@@ -616,7 +619,7 @@ public class PropertyUtils {
*/
@Nullable
@Override
public Long getMin() {
public Number getMin() {
if (RANGE_ANNOTATION != null) {
@@ -627,7 +630,19 @@ public class PropertyUtils {
}
}
return getAnnotationAttribute(Min.class, "value", Long.class).orElse(null);
Optional<Long> minLong = getAnnotationAttribute(Min.class, "value", Long.class);
if (minLong.isPresent()) {
return minLong.get();
}
Optional<String> minDecimal = getAnnotationAttribute(DecimalMin.class, "value", String.class);
if (minDecimal.isPresent()) {
return new BigDecimal(minDecimal.get());
}
return null;
}
/*
@@ -636,7 +651,7 @@ public class PropertyUtils {
*/
@Nullable
@Override
public Long getMax() {
public Number getMax() {
if (RANGE_ANNOTATION != null) {
@@ -647,7 +662,19 @@ public class PropertyUtils {
}
}
return getAnnotationAttribute(Max.class, "value", Long.class).orElse(null);
Optional<Long> maxLong = getAnnotationAttribute(Max.class, "value", Long.class);
if (maxLong.isPresent()) {
return maxLong.get();
}
Optional<String> maxDecimal = getAnnotationAttribute(DecimalMax.class, "value", String.class);
if (maxDecimal.isPresent()) {
return new BigDecimal(maxDecimal.get());
}
return null;
}
/*

View File

@@ -42,7 +42,8 @@ final class HalFormsProperty implements Named {
private final Object value;
private final boolean templated, multi;
private final @JsonInclude(Include.NON_DEFAULT) boolean readOnly, required;
private final @Nullable Long min, max, minLength, maxLength;
private final @Nullable Number min, max;
private final @Nullable Long minLength, maxLength;
private final @Nullable HtmlInputType type;
private final @Nullable HalFormsOptions options;
@@ -67,7 +68,7 @@ final class HalFormsProperty implements Named {
private HalFormsProperty(String name, boolean readOnly, @Nullable Object value, String prompt, String regex,
boolean templated,
boolean required, boolean multi, String placeholder, @Nullable Long min, @Nullable Long max,
boolean required, boolean multi, String placeholder, @Nullable Number min, @Nullable Number max,
@Nullable Long minLength, @Nullable Long maxLength, @Nullable HtmlInputType type,
@Nullable HalFormsOptions options) {
@@ -237,7 +238,7 @@ final class HalFormsProperty implements Named {
* @param min can be {@literal null}
* @return will never be {@literal null}.
*/
HalFormsProperty withMin(@Nullable Long min) {
HalFormsProperty withMin(@Nullable Number min) {
return Objects.equals(this.min, min) ? this
: new HalFormsProperty(this.name, this.readOnly, this.value, this.prompt, this.regex, this.templated,
@@ -251,7 +252,7 @@ final class HalFormsProperty implements Named {
* @param max can be {@literal null}
* @return will never be {@literal null}.
*/
HalFormsProperty withMax(@Nullable Long max) {
HalFormsProperty withMax(@Nullable Number max) {
return Objects.equals(this.max, max) ? this
: new HalFormsProperty(this.name, this.readOnly, this.value, this.prompt, this.regex, this.templated,
@@ -375,7 +376,7 @@ final class HalFormsProperty implements Named {
*/
@Nullable
@JsonProperty
public Long getMin() {
public Number getMin() {
return min;
}
@@ -384,7 +385,7 @@ final class HalFormsProperty implements Named {
*/
@Nullable
@JsonProperty
public Long getMax() {
public Number getMax() {
return max;
}

View File

@@ -19,11 +19,14 @@ import static org.assertj.core.api.Assertions.*;
import lombok.Getter;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@@ -124,6 +127,22 @@ class HalFormsTemplateBuilderUnitTest {
assertThat(text).map(HalFormsProperty::getMaxLength).hasValue(5L);
}
@Test // #1557
void considersDecimalMinandDecimalMaxAnnotations() {
Link link = Affordances.of(Link.of("/example")) //
.afford(HttpMethod.POST) //
.withInput(Payload.class) //
.toLink();
HalFormsTemplate template = new HalFormsTemplateBuilder(new HalFormsConfiguration(), //
MessageResolver.DEFAULTS_ONLY).findTemplates(new RepresentationModel<>().add(link)).get("default");
Optional<HalFormsProperty> decimal = template.getPropertyByName("decimal");
assertThat(decimal).map(HalFormsProperty::getMin).hasValue(new BigDecimal("2.1"));
assertThat(decimal).map(HalFormsProperty::getMax).hasValue(new BigDecimal("5.3"));
}
@Test // #1427
void addsTargetAttributeForLinksNotPointingToSelf() {
@@ -238,5 +257,9 @@ class HalFormsTemplateBuilderUnitTest {
@Range(min = 8, max = 10) //
Integer range;
@DecimalMin("2.1") //
@DecimalMax("5.3") //
BigDecimal decimal;
}
}