#1697 - Support for custom property types in HAL FORMS.

As per the discussion in mamund/hal-forms#88, the list of property types in HAL FORMS is not exhaustive. This means we cannot align them with HTML input types and even more so not drop anything not an HTML input type.

This is now implemented by switching to generic Strings in the HAL FORMS property DTO and directly piping the value originally registered on the affordance into it.
This commit is contained in:
Oliver Drotbohm
2021-11-02 00:15:11 +01:00
parent 07125638f4
commit c487fcc802
3 changed files with 30 additions and 8 deletions

View File

@@ -19,7 +19,6 @@ import java.util.Objects;
import java.util.Optional;
import org.springframework.hateoas.AffordanceModel.Named;
import org.springframework.hateoas.mediatype.html.HtmlInputType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -44,7 +43,7 @@ final class HalFormsProperty implements Named {
private final @JsonInclude(Include.NON_DEFAULT) boolean readOnly, required;
private final @Nullable Number min, max;
private final @Nullable Long minLength, maxLength;
private final @Nullable HtmlInputType type;
private final @Nullable String type;
private final @Nullable HalFormsOptions options;
HalFormsProperty() {
@@ -69,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 Number min, @Nullable Number max,
@Nullable Long minLength, @Nullable Long maxLength, @Nullable HtmlInputType type,
@Nullable Long minLength, @Nullable Long maxLength, @Nullable String type,
@Nullable HalFormsOptions options) {
Assert.notNull(name, "Name must not be null!");
@@ -294,7 +293,7 @@ final class HalFormsProperty implements Named {
* @param type can be {@literal null}
* @return will never be {@literal null}.
*/
HalFormsProperty withType(@Nullable HtmlInputType type) {
HalFormsProperty withType(@Nullable String type) {
return Objects.equals(this.type, type) ? this
: new HalFormsProperty(this.name, this.readOnly, this.value, this.prompt, this.regex, this.templated,
@@ -413,7 +412,7 @@ final class HalFormsProperty implements Named {
@Nullable
@JsonProperty
@JsonInclude(Include.NON_NULL)
HtmlInputType getType() {
String getType() {
return type;
}

View File

@@ -29,7 +29,6 @@ import org.springframework.context.MessageSourceResolvable;
import org.springframework.hateoas.AffordanceModel.InputPayloadMetadata;
import org.springframework.hateoas.AffordanceModel.PropertyMetadata;
import org.springframework.hateoas.mediatype.MessageResolver;
import org.springframework.hateoas.mediatype.html.HtmlInputType;
import org.springframework.http.HttpMethod;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
@@ -84,8 +83,7 @@ class HalFormsPropertyFactory {
return model.createProperties((payload, metadata) -> {
String inputTypeSource = metadata.getInputType();
HtmlInputType inputType = inputTypeSource == null ? null : HtmlInputType.of(inputTypeSource);
String inputType = metadata.getInputType();
HalFormsOptions options = optionsFactory.getOptions(payload, metadata);
HalFormsProperty property = new HalFormsProperty()

View File

@@ -37,6 +37,7 @@ import org.hibernate.validator.constraints.Range;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.hateoas.InputType;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.RepresentationModel;
@@ -246,6 +247,23 @@ class HalFormsTemplateBuilderUnitTest {
assertThat(templates.get("default").getTarget()).endsWith("/example");
}
@Test // #1697
void exposesCustomInputType() {
RepresentationModel<?> models = new RepresentationModel<>(
Affordances.of(Link.of("/example", LinkRelation.of("example"))) //
.afford(HttpMethod.POST) //
.withInput(WithCustomInputType.class) //
.toLink());
Map<String, HalFormsTemplate> templates = new HalFormsTemplateBuilder(new HalFormsConfiguration(),
MessageResolver.DEFAULTS_ONLY).findTemplates(models);
assertThat(templates.get("default").getPropertyByName("property")).hasValueSatisfying(it -> {
assertThat(it.getType()).isEqualTo("custom");
});
}
@Getter
static class PatternExample extends RepresentationModel<PatternExample> {
@@ -283,4 +301,11 @@ class HalFormsTemplateBuilderUnitTest {
@DecimalMax("5.3") //
BigDecimal decimal;
}
@Getter
static class WithCustomInputType {
@InputType("custom") //
String property;
}
}