From c487fcc802090b5a3002e0c8287ee02446a58b70 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 2 Nov 2021 00:15:11 +0100 Subject: [PATCH] #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. --- .../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 e6eb5cfe..f52a672b 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; @@ -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; } 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 48f8c138..a3a66773 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 @@ -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 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 { @@ -283,4 +301,11 @@ class HalFormsTemplateBuilderUnitTest { @DecimalMax("5.3") // BigDecimal decimal; } + + @Getter + static class WithCustomInputType { + + @InputType("custom") // + String property; + } }