From b7577e026e90529dee88bfd580871210974f5a61 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 2 Nov 2021 00:15:11 +0100 Subject: [PATCH] #1699 - 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. --- .../mediatype/hal/forms/HalFormsProperty.java | 9 +++---- .../hal/forms/HalFormsPropertyFactory.java | 4 +-- .../HalFormsTemplateBuilderUnitTest.java | 25 +++++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) 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 1cab152f..3dc2d38a 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 @@ -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; @@ -43,7 +42,7 @@ final class HalFormsProperty implements Named { 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 HtmlInputType type; + private final @Nullable String type; private final @Nullable HalFormsOptions options; HalFormsProperty() { @@ -68,7 +67,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, - @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!"); @@ -293,7 +292,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, @@ -412,7 +411,7 @@ final class HalFormsProperty implements Named { @Nullable @JsonProperty @JsonInclude(Include.NON_NULL) - HtmlInputType getType() { + String getType() { return type; } diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsPropertyFactory.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsPropertyFactory.java index 22754a2f..0054f1c1 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsPropertyFactory.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsPropertyFactory.java @@ -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() 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 e74a8384..28f56500 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 @@ -34,6 +34,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; @@ -206,6 +207,23 @@ class HalFormsTemplateBuilderUnitTest { }); } + @Test // #1697 + void exposesCustomInputType() { + + RepresentationModel models = new RepresentationModel<>( + Affordances.of(Link.of("/example", LinkRelation.of("example"))) // + .afford(HttpMethod.POST) // + .withInput(WithCustomInputType.class) // + .toLink()); + + Map 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 { @@ -239,4 +257,11 @@ class HalFormsTemplateBuilderUnitTest { @Range(min = 8, max = 10) // Integer range; } + + @Getter + static class WithCustomInputType { + + @InputType("custom") // + String property; + } }