diff --git a/pom.xml b/pom.xml
index b4bc4ebf..2532263e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -895,6 +895,13 @@
2.0.1.Final
true
+
+
+ org.hibernate.validator
+ hibernate-validator
+ 6.1.7.Final
+ test
+
org.projectlombok
diff --git a/src/main/java/org/springframework/hateoas/AffordanceModel.java b/src/main/java/org/springframework/hateoas/AffordanceModel.java
index 370b8920..897c1b02 100644
--- a/src/main/java/org/springframework/hateoas/AffordanceModel.java
+++ b/src/main/java/org/springframework/hateoas/AffordanceModel.java
@@ -137,6 +137,22 @@ public abstract class AffordanceModel {
return this.output;
}
+ /**
+ * Creates a {@link List} of properties based on the given creator.
+ *
+ * @param the property type
+ * @param creator a creator function that turns an {@link InputPayloadMetadata} and {@link PropertyMetadata} into a
+ * property instance.
+ * @return will never be {@literal null}.
+ * @since 1.3
+ */
+ public List createProperties(BiFunction creator) {
+
+ return input.stream()
+ .map(it -> creator.apply(input, it))
+ .collect(Collectors.toList());
+ }
+
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
@@ -208,39 +224,15 @@ public abstract class AffordanceModel {
: DelegatingInputPayloadMetadata.of(metadata);
}
- /**
- * Creates a {@link List} of properties based on the given creator and customizer. The {@link PropertyMetadata} will
- * be applied to the instance returned from the creator before its handed to the customizer.
- *
- * @param the property type
- * @param creator a creator function that turns a {@link PropertyMetadata} into a property instance.
- * @param customizer a {@link BiFunction} to apply after the {@link PropertyMetadata} has been applied to the
- * property instance.
- * @return will never be {@literal null}.
- */
- default & Named> List createProperties(
- Function creator,
- BiFunction customizer) {
-
- Assert.notNull(creator, "Creator must not be null!");
- Assert.notNull(customizer, "Customizer must not be null!");
-
- return stream().map(creator).map(it -> {
-
- return getPropertyMetadata(it.getName())
- .map(metadata -> customizer.apply(it.apply(metadata), metadata))
- .orElse(it);
-
- }).collect(Collectors.toList());
- }
-
/**
* Applies the {@link InputPayloadMetadata} to the given target.
*
* @param
* @param target
* @return
+ * @deprecated since 1.3, prefer setting up the model types via {@link #createProperties(Function)}
*/
+ @Deprecated
default & Named> T applyTo(T target) {
return getPropertyMetadata(target.getName()) //
@@ -396,6 +388,50 @@ public abstract class AffordanceModel {
* @return
*/
ResolvableType getType();
+
+ /**
+ * Return the minimum value allowed for a numeric type.
+ *
+ * @return can be {@literal null}.
+ * @since 1.3
+ */
+ @Nullable
+ default Long getMin() {
+ return null;
+ }
+
+ /**
+ * Return the maximum value allowed for a numeric type.
+ *
+ * @return can be {@literal null}.
+ * @since 1.3
+ */
+ @Nullable
+ default Long getMax() {
+ return null;
+ }
+
+ /**
+ * Return the minimum length allowed for a string type.
+ *
+ * @return can be {@literal null}.
+ * @since 1.3
+ */
+ @Nullable
+ default Long getMinLength() {
+ return null;
+ }
+
+ /**
+ * Return the maximum length allowed for a string type.
+ *
+ * @return can be {@literal null}.
+ * @since 1.3
+ */
+ @Nullable
+ default Long getMaxLength() {
+ return null;
+ }
}
/**
diff --git a/src/main/java/org/springframework/hateoas/mediatype/PropertyUtils.java b/src/main/java/org/springframework/hateoas/mediatype/PropertyUtils.java
index cc392efe..ecfe90ea 100644
--- a/src/main/java/org/springframework/hateoas/mediatype/PropertyUtils.java
+++ b/src/main/java/org/springframework/hateoas/mediatype/PropertyUtils.java
@@ -20,21 +20,13 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@@ -56,7 +48,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
-import org.springframework.util.StringUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -512,6 +503,14 @@ public class PropertyUtils {
*/
private static class Jsr303AwarePropertyMetadata extends DefaultPropertyMetadata {
+ private static final Optional> LENGTH_ANNOTATION;
+
+ static {
+
+ LENGTH_ANNOTATION = Optional.ofNullable(org.springframework.hateoas.support.ClassUtils
+ .loadIfPresent("org.hibernate.validator.constraints.Length"));
+ }
+
private final AnnotatedProperty property;
/**
@@ -541,24 +540,65 @@ public class PropertyUtils {
*/
@Override
public Optional getPattern() {
-
- MergedAnnotation annotation = property.getAnnotation(Pattern.class);
-
- if (annotation.isPresent()) {
- return fromAnnotation(annotation);
- }
-
- annotation = property.getTypeAnnotations().get(Pattern.class);
-
- return annotation.isPresent() //
- ? fromAnnotation(annotation) //
- : Optional.empty();
+ return getAnnotationAttribute(Pattern.class, "regexp", String.class);
}
- private static Optional fromAnnotation(MergedAnnotation annotation) {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.hateoas.AffordanceModel.PropertyMetadata#getMin()
+ */
+ @Nullable
+ @Override
+ public Long getMin() {
+ return getAnnotationAttribute(Min.class, "value", Long.class).orElse(null);
+ }
- return Optional.of(annotation.getString("regexp")) //
- .filter(StringUtils::hasText);
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.hateoas.AffordanceModel.PropertyMetadata#getMax()
+ */
+ @Nullable
+ @Override
+ public Long getMax() {
+ return getAnnotationAttribute(Max.class, "value", Long.class).orElse(null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.hateoas.AffordanceModel.PropertyMetadata#getMinLength()
+ */
+ @Nullable
+ @Override
+ public Long getMinLength() {
+ return LENGTH_ANNOTATION.flatMap(it -> getAnnotationAttribute(it, "min", Integer.class))
+ .map(Integer::longValue)
+ .orElse(null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.hateoas.AffordanceModel.PropertyMetadata#getMaxLength()
+ */
+ @Nullable
+ @Override
+ public Long getMaxLength() {
+ return LENGTH_ANNOTATION.flatMap(it -> getAnnotationAttribute(it, "max", Integer.class))
+ .map(Integer::longValue)
+ .orElse(null);
+ }
+
+ private Optional getAnnotationAttribute(Class extends Annotation> annotation, String attribute,
+ Class type) {
+
+ MergedAnnotation extends Annotation> mergedAnnotation = property.getAnnotation(annotation);
+
+ if (mergedAnnotation.isPresent()) {
+ return mergedAnnotation.getValue(attribute, type);
+ }
+
+ mergedAnnotation = property.getTypeAnnotations().get(annotation);
+
+ return mergedAnnotation.isPresent() ? mergedAnnotation.getValue(attribute, type) : Optional.empty();
}
}
}
diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java
index 221e45e7..ad889664 100644
--- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java
+++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java
@@ -15,27 +15,13 @@
*/
package org.springframework.hateoas.mediatype.hal.forms;
-import static org.springframework.http.HttpMethod.*;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import org.springframework.context.MessageSourceResolvable;
import org.springframework.hateoas.AffordanceModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.QueryParameter;
-import org.springframework.hateoas.mediatype.MessageResolver;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
-import org.springframework.util.StringUtils;
/**
* {@link AffordanceModel} for a HAL-FORMS {@link MediaType}.
@@ -45,121 +31,8 @@ import org.springframework.util.StringUtils;
*/
class HalFormsAffordanceModel extends AffordanceModel {
- private static final Set ENTITY_ALTERING_METHODS = EnumSet.of(POST, PUT, PATCH);
-
public HalFormsAffordanceModel(String name, Link link, HttpMethod httpMethod, InputPayloadMetadata inputType,
List queryMethodParameters, PayloadMetadata outputType) {
super(name, link, httpMethod, inputType, queryMethodParameters, outputType);
}
-
- /**
- * Applies the given customizer to all {@link HalFormsProperty} of this model.
- *
- * @param customizer must not be {@literal null}.
- * @return
- */
- public List getProperties(HalFormsConfiguration configuration, MessageResolver resolver) {
-
- if (!ENTITY_ALTERING_METHODS.contains(getHttpMethod())) {
- return Collections.emptyList();
- }
-
- Function creator = it -> {
-
- HalFormsProperty property = new HalFormsProperty().withName(it.getName());
-
- return configuration.getTypePatternFor(it.getType()) //
- .map(property::withRegex) //
- .orElse(property);
- };
-
- return getInput().createProperties(creator, (property, metadata) -> {
-
- return Optional.of(property)
- .map(it -> apply(it, I18nedPlaceholder::of, it::withPlaceholder, resolver))
- .map(it -> apply(it, I18nedPropertyPrompt::of, it::withPrompt, resolver))
- .map(it -> hasHttpMethod(HttpMethod.PATCH) ? it.withRequired(false) : it)
- .orElse(property);
- });
- }
-
- private HalFormsProperty apply(HalFormsProperty property,
- BiFunction creator,
- Function application, MessageResolver resolver) {
-
- InputPayloadMetadata metadata = getInput();
- I18nedPropertyMetadata source = creator.apply(metadata, property);
- String resolved = resolver.resolve(source);
-
- return !StringUtils.hasText(resolved)
- ? property
- : application.apply(resolved);
- }
-
- private static class I18nedPropertyMetadata implements MessageSourceResolvable {
-
- private final String template;
- private final InputPayloadMetadata metadata;
- private final HalFormsProperty property;
-
- protected I18nedPropertyMetadata(String template, InputPayloadMetadata metadata, HalFormsProperty property) {
-
- this.template = template;
- this.metadata = metadata;
- this.property = property;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.context.MessageSourceResolvable#getDefaultMessage()
- */
- @Nullable
- @Override
- public String getDefaultMessage() {
- return "";
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.context.MessageSourceResolvable#getCodes()
- */
- @NonNull
- @Override
- public String[] getCodes() {
-
- String globalCode = String.format(template, property.getName());
-
- List codes = new ArrayList<>();
-
- metadata.getI18nCodes().stream() //
- .map(it -> String.format("%s.%s", it, globalCode)) //
- .forEach(codes::add);
-
- codes.add(globalCode);
-
- return codes.toArray(new String[0]);
- }
- }
-
- private static class I18nedPropertyPrompt extends I18nedPropertyMetadata {
-
- private I18nedPropertyPrompt(InputPayloadMetadata metadata, HalFormsProperty property) {
- super("%s._prompt", metadata, property);
- }
-
- public static I18nedPropertyPrompt of(InputPayloadMetadata metadata, HalFormsProperty property) {
- return new I18nedPropertyPrompt(metadata, property);
- }
- }
-
- private static class I18nedPlaceholder extends I18nedPropertyMetadata {
-
- private I18nedPlaceholder(InputPayloadMetadata metadata, HalFormsProperty property) {
- super("%s._placeholder", metadata, property);
- }
-
- public static I18nedPlaceholder of(InputPayloadMetadata metadata, HalFormsProperty property) {
- return new I18nedPlaceholder(metadata, property);
- }
- }
}
diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsProperty.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsProperty.java
index 66502009..4b3d67c5 100644
--- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsProperty.java
+++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsProperty.java
@@ -16,10 +16,9 @@
package org.springframework.hateoas.mediatype.hal.forms;
import java.util.Objects;
+import java.util.Optional;
import org.springframework.hateoas.AffordanceModel.Named;
-import org.springframework.hateoas.AffordanceModel.PropertyMetadata;
-import org.springframework.hateoas.AffordanceModel.PropertyMetadataConfigured;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -36,11 +35,12 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @see https://mamund.site44.com/misc/hal-forms/
*/
@JsonInclude(Include.NON_DEFAULT)
-final class HalFormsProperty implements PropertyMetadataConfigured, Named {
+final class HalFormsProperty implements Named {
private final String name, value, prompt, regex, placeholder;
private final boolean templated, multi;
private final @JsonInclude(Include.NON_DEFAULT) boolean readOnly, required;
+ private final @Nullable Long min, max, minLength, maxLength;
HalFormsProperty() {
@@ -53,10 +53,15 @@ final class HalFormsProperty implements PropertyMetadataConfigured regex) {
+ return regex.map(it -> withRegex(it)).orElse(this);
}
/**
@@ -172,7 +177,7 @@ final class HalFormsProperty implements PropertyMetadataConfigured ENTITY_ALTERING_METHODS = EnumSet.of(POST, PUT, PATCH);
+
+ private final HalFormsConfiguration configuration;
+ private final MessageResolver resolver;
+
+ /**
+ * Creates a new {@link HalFormsPropertyFactory} for the given {@link HalFormsConfiguration} and
+ * {@link MessageResolver}.
+ *
+ * @param configuration must not be {@literal null}.
+ * @param resolver must not be {@literal null}.
+ */
+ public HalFormsPropertyFactory(HalFormsConfiguration configuration, MessageResolver resolver) {
+
+ Assert.notNull(configuration, "HalFormsConfiguration must not be null!");
+ Assert.notNull(resolver, "MessageResolver must not be null!");
+
+ this.configuration = configuration;
+ this.resolver = resolver;
+ }
+
+ /**
+ * Creates {@link HalFormsProperty} from the given {@link HalFormsAffordanceModel}.
+ *
+ * @param model must not be {@literal null}.
+ * @return
+ */
+ public List createProperties(HalFormsAffordanceModel model) {
+
+ Assert.notNull(model, "HalFormsModel must not be null!");
+
+ if (!ENTITY_ALTERING_METHODS.contains(model.getHttpMethod())) {
+ return Collections.emptyList();
+ }
+
+ return model.createProperties((payload, metadata) -> {
+
+ HalFormsProperty property = new HalFormsProperty()
+ .withName(metadata.getName())
+ .withRequired(metadata.isRequired()) //
+ .withReadOnly(metadata.isReadOnly())
+ .withMin(metadata.getMin())
+ .withMax(metadata.getMax())
+ .withMinLength(metadata.getMinLength())
+ .withMaxLength(metadata.getMaxLength())
+ .withRegex(lookupRegex(metadata));
+
+ Function factory = I18nedPropertyMetadata.factory(payload, property);
+
+ return Optional.of(property)
+ .map(it -> i18n(it, factory.apply("_placeholder"), it::withPlaceholder))
+ .map(it -> i18n(it, factory.apply("_prompt"), it::withPrompt))
+ .map(it -> model.hasHttpMethod(HttpMethod.PATCH) ? it.withRequired(false) : it)
+ .orElse(property);
+ });
+ }
+
+ private Optional lookupRegex(PropertyMetadata metadata) {
+
+ Optional pattern = metadata.getPattern();
+
+ if (pattern.isPresent()) {
+ return pattern;
+ }
+
+ return configuration.getTypePatternFor(metadata.getType());
+ }
+
+ private HalFormsProperty i18n(HalFormsProperty property, MessageSourceResolvable metadata,
+ Function application) {
+
+ String resolved = resolver.resolve(metadata);
+
+ return !StringUtils.hasText(resolved)
+ ? property
+ : application.apply(resolved);
+ }
+
+ private static class I18nedPropertyMetadata implements MessageSourceResolvable {
+
+ private final String template;
+ private final InputPayloadMetadata metadata;
+ private final HalFormsProperty property;
+
+ private I18nedPropertyMetadata(String template, InputPayloadMetadata metadata, HalFormsProperty property) {
+
+ this.template = template;
+ this.metadata = metadata;
+ this.property = property;
+ }
+
+ public static Function factory(InputPayloadMetadata metadata,
+ HalFormsProperty property) {
+ return suffix -> new I18nedPropertyMetadata("%s.".concat(suffix), metadata, property);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.context.MessageSourceResolvable#getDefaultMessage()
+ */
+ @Nullable
+ @Override
+ public String getDefaultMessage() {
+ return "";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.context.MessageSourceResolvable#getCodes()
+ */
+ @NonNull
+ @Override
+ public String[] getCodes() {
+
+ String globalCode = String.format(template, property.getName());
+
+ List codes = new ArrayList<>();
+
+ metadata.getI18nCodes().stream() //
+ .map(it -> String.format("%s.%s", it, globalCode)) //
+ .forEach(codes::add);
+
+ codes.add(globalCode);
+
+ return codes.toArray(new String[0]);
+ }
+ }
+}
diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java
index f96c3307..56b708ff 100644
--- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java
+++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java
@@ -37,13 +37,13 @@ import org.springframework.util.Assert;
class HalFormsTemplateBuilder {
- private final HalFormsConfiguration configuration;
private final MessageResolver resolver;
+ private final HalFormsPropertyFactory factory;
public HalFormsTemplateBuilder(HalFormsConfiguration configuration, MessageResolver resolver) {
- this.configuration = configuration;
this.resolver = resolver;
+ this.factory = new HalFormsPropertyFactory(configuration, resolver);
}
/**
@@ -73,7 +73,7 @@ class HalFormsTemplateBuilder {
.forEach(it -> {
HalFormsTemplate template = HalFormsTemplate.forMethod(it.getHttpMethod()) //
- .withProperties(it.getProperties(configuration, resolver));
+ .withProperties(factory.createProperties(it));
template = applyTo(template, TemplateTitle.of(it, templates.isEmpty()));
templates.put(templates.isEmpty() ? "default" : it.getName(), template);
diff --git a/src/main/java/org/springframework/hateoas/support/ClassUtils.java b/src/main/java/org/springframework/hateoas/support/ClassUtils.java
new file mode 100644
index 00000000..0a3caa73
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/support/ClassUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.hateoas.support;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * @author Oliver Drotbohm
+ */
+public class ClassUtils {
+
+ @Nullable
+ @SuppressWarnings("unchecked")
+ public static Class loadIfPresent(String type) {
+
+ try {
+ return (Class) org.springframework.util.ClassUtils.forName(type,
+ org.springframework.hateoas.support.ClassUtils.class.getClassLoader());
+ } catch (ClassNotFoundException | LinkageError e) {
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java
index 81ba7ad6..da32aea7 100644
--- a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java
+++ b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java
@@ -20,10 +20,14 @@ import static org.assertj.core.api.Assertions.*;
import lombok.Getter;
import java.util.Map;
+import java.util.Optional;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
+import org.hibernate.validator.constraints.Length;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@@ -66,10 +70,6 @@ class HalFormsTemplateBuilderUnitTest {
@Test
void allPropertiesAreOptionalForPatchRequests() throws Exception {
- Affordances.of(Link.of("/example")) //
- .afford(HttpMethod.PATCH) //
- .withInput(RequiredProperty.class);
-
RequiredProperty model = new RequiredProperty();
model.add(Affordances.of(Link.of("/example")) //
.afford(HttpMethod.PATCH) //
@@ -95,6 +95,26 @@ class HalFormsTemplateBuilderUnitTest {
assertThat(template.getPropertyByName("name").map(HalFormsProperty::isRequired)).hasValue(true);
}
+ @Test // #1439
+ void considersMinandMaxAnnotations() {
+
+ 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 name = template.getPropertyByName("number");
+ assertThat(name).map(HalFormsProperty::getMin).hasValue(2L);
+ assertThat(name).map(HalFormsProperty::getMax).hasValue(5L);
+
+ Optional text = template.getPropertyByName("text");
+ assertThat(text).map(HalFormsProperty::getMinLength).hasValue(2L);
+ assertThat(text).map(HalFormsProperty::getMaxLength).hasValue(5L);
+ }
+
@Getter
static class PatternExample extends RepresentationModel {
@@ -114,4 +134,15 @@ class HalFormsTemplateBuilderUnitTest {
static class RequiredProperty extends RepresentationModel {
@NotNull String name;
}
+
+ @Getter
+ static class Payload {
+
+ @Min(2) //
+ @Max(5) //
+ Integer number;
+
+ @Length(min = 2, max = 5) //
+ String text;
+ }
}