#1510 - Support for defining the selected value on HalFormsOptions.

We now expose a HalFormsOptions.withSelectedValue(…) and propagate this into the HAL FORMS property field named "value".
This commit is contained in:
Oliver Drotbohm
2021-04-07 19:39:23 +02:00
parent 39775148dd
commit bbb8f426fe
4 changed files with 82 additions and 27 deletions

View File

@@ -23,6 +23,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -61,7 +62,7 @@ public interface HalFormsOptions {
Assert.notNull(values, "Values must not be null!");
return new Inline(values, null, null, null, null);
return new Inline(values, null, null, null, null, null);
}
/**
@@ -74,7 +75,7 @@ public interface HalFormsOptions {
Assert.notNull(link, "Link must not be null!");
return new Remote(link, null, null, null, null);
return new Remote(link, null, null, null, null, null);
}
/**
@@ -122,14 +123,18 @@ public interface HalFormsOptions {
@Nullable
Long getMaxItems();
@Nullable
Object getSelectedValue();
public static abstract class AbstractHalFormsOptions<T extends AbstractHalFormsOptions<T>>
implements HalFormsOptions {
private final @Nullable String promptField, valueField;
private final @Nullable Long minItems, maxItems;
private final @Nullable Object selectedValue;
protected AbstractHalFormsOptions(@Nullable String promptRef, @Nullable String valueRef, @Nullable Long minItems,
@Nullable Long maxItems) {
@Nullable Long maxItems, @Nullable Object selectedValue) {
Assert.isTrue(minItems == null || minItems >= 0, "MinItems must be greater than or equal to 0!");
@@ -137,6 +142,7 @@ public interface HalFormsOptions {
this.valueField = valueRef;
this.minItems = minItems;
this.maxItems = maxItems;
this.selectedValue = selectedValue;
}
/*
@@ -179,6 +185,17 @@ public interface HalFormsOptions {
return maxItems;
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.mediatype.hal.forms.HalFormsOptions#getSelectedValue()
*/
@Nullable
@Override
@JsonIgnore
public Object getSelectedValue() {
return selectedValue;
}
/**
* Configures the given field to be used as prompt field.
*
@@ -191,7 +208,7 @@ public interface HalFormsOptions {
throw new IllegalArgumentException("Prompt field has to either be null or actually have text!");
}
return with(promptField, valueField, minItems, maxItems);
return with(promptField, valueField, minItems, maxItems, selectedValue);
}
/**
@@ -206,7 +223,7 @@ public interface HalFormsOptions {
throw new IllegalArgumentException("Value field has to either be null or actually have text!");
}
return with(promptField, valueField, minItems, maxItems);
return with(promptField, valueField, minItems, maxItems, selectedValue);
}
/**
@@ -221,7 +238,7 @@ public interface HalFormsOptions {
throw new IllegalArgumentException("minItems has to be null or greater or equal to zero!");
}
return with(promptField, valueField, minItems, maxItems);
return with(promptField, valueField, minItems, maxItems, selectedValue);
}
/**
@@ -230,13 +247,23 @@ public interface HalFormsOptions {
* @param maxItems must be {@literal null} or greater than zero.
* @return
*/
public T withMaxItems(Long maxItems) {
public T withMaxItems(@Nullable Long maxItems) {
if (maxItems != null && maxItems <= 0) {
throw new IllegalArgumentException("maxItems has to be null or greater than zero!");
}
return with(promptField, valueField, minItems, maxItems);
return with(promptField, valueField, minItems, maxItems, selectedValue);
}
/**
* Configured the value to be initially selected
*
* @param value
* @return
*/
public T withSelectedValue(@Nullable Object value) {
return with(promptField, valueField, minItems, maxItems, value);
}
/**
@@ -249,7 +276,7 @@ public interface HalFormsOptions {
* @return
*/
protected abstract T with(@Nullable String promptRef, @Nullable String valueRef, @Nullable Long minItems,
@Nullable Long maxItems);
@Nullable Long maxItems, @Nullable Object selectedValue);
}
public static class Inline extends AbstractHalFormsOptions<Inline> {
@@ -262,9 +289,9 @@ public interface HalFormsOptions {
* @param valueRef
*/
private Inline(Collection<? extends Object> values, @Nullable String promptRef, @Nullable String valueRef,
@Nullable Long minItems, @Nullable Long maxItems) {
@Nullable Long minItems, @Nullable Long maxItems, @Nullable Object selectedValue) {
super(promptRef, valueRef, minItems, maxItems);
super(promptRef, valueRef, minItems, maxItems, selectedValue);
Assert.notNull(values, "Values must not be null!");
@@ -283,12 +310,12 @@ public interface HalFormsOptions {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.mediatype.hal.forms.HalFormsOptions.AbstractHalFormsOptions#with(java.lang.String, java.lang.String, java.lang.Long, java.lang.Long)
* @see org.springframework.hateoas.mediatype.hal.forms.HalFormsOptions.AbstractHalFormsOptions#with(java.lang.String, java.lang.String, java.lang.Long, java.lang.Long, java.lang.Object)
*/
@Override
protected Inline with(@Nullable String promptRef, @Nullable String valueRef, @Nullable Long minItems,
@Nullable Long maxItems) {
return new Inline(inline, promptRef, valueRef, minItems, maxItems);
@Nullable Long maxItems, @Nullable Object selectedValue) {
return new Inline(inline, promptRef, valueRef, minItems, maxItems, selectedValue);
}
}
@@ -302,9 +329,9 @@ public interface HalFormsOptions {
private final Link link;
private Remote(Link link, @Nullable String promptRef, @Nullable String valueRef, @Nullable Long minItems,
@Nullable Long maxItems) {
@Nullable Long maxItems, @Nullable Object selectedValue) {
super(promptRef, valueRef, minItems, maxItems);
super(promptRef, valueRef, minItems, maxItems, selectedValue);
Assert.notNull(link, "Link must not be null!");
@@ -323,12 +350,12 @@ public interface HalFormsOptions {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.mediatype.hal.forms.HalFormsOptions.Foo#withFoo(java.lang.String, java.lang.String)
* @see org.springframework.hateoas.mediatype.hal.forms.HalFormsOptions.AbstractHalFormsOptions#with(java.lang.String, java.lang.String, java.lang.Long, java.lang.Long, java.lang.Object)
*/
@Override
protected Remote with(@Nullable String promptRef, @Nullable String valueRef, @Nullable Long minItems,
@Nullable Long maxItems) {
return new Remote(link, promptRef, valueRef, minItems, maxItems);
@Nullable Long maxItems, @Nullable Object selectedValue) {
return new Remote(link, promptRef, valueRef, minItems, maxItems, selectedValue);
}
}
}

View File

@@ -38,7 +38,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_DEFAULT)
final class HalFormsProperty implements Named {
private final String name, value, prompt, regex, placeholder;
private final String name, prompt, regex, placeholder;
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;
@@ -64,12 +65,13 @@ final class HalFormsProperty implements Named {
this.options = null;
}
private HalFormsProperty(String name, boolean readOnly, String value, String prompt, String regex, boolean templated,
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 HalFormsOptions options) {
Assert.notNull(name, "name must not be null!");
Assert.notNull(name, "Name must not be null!");
this.name = name;
this.readOnly = readOnly;
@@ -132,7 +134,7 @@ final class HalFormsProperty implements Named {
* @param value
* @return
*/
HalFormsProperty withValue(String value) {
HalFormsProperty withValue(@Nullable Object value) {
return this.value == value ? this
: new HalFormsProperty(this.name, this.readOnly, value, this.prompt, this.regex, this.templated, this.required,
@@ -329,7 +331,7 @@ final class HalFormsProperty implements Named {
}
@JsonProperty
String getValue() {
Object getValue() {
return this.value;
}

View File

@@ -80,12 +80,13 @@ class HalFormsPropertyFactory {
return Collections.emptyList();
}
HalFormsOptionsFactory options = configuration.getOptionsFactory();
HalFormsOptionsFactory optionsFactory = configuration.getOptionsFactory();
return model.createProperties((payload, metadata) -> {
String inputTypeSource = metadata.getInputType();
HtmlInputType inputType = inputTypeSource == null ? null : HtmlInputType.of(inputTypeSource);
HalFormsOptions options = optionsFactory.getOptions(payload, metadata);
HalFormsProperty property = new HalFormsProperty()
.withName(metadata.getName())
@@ -97,7 +98,8 @@ class HalFormsPropertyFactory {
.withMaxLength(metadata.getMaxLength())
.withRegex(lookupRegex(metadata)) //
.withType(inputType) //
.withOptions(options.getOptions(payload, metadata));
.withValue(options != null ? options.getSelectedValue() : null) //
.withOptions(options);
Function<String, I18nedPropertyMetadata> factory = I18nedPropertyMetadata.factory(payload, property);

View File

@@ -158,7 +158,7 @@ class HalFormsTemplateBuilderUnitTest {
}
@Test // #1483
void rendersRegisteredSuggest() {
void rendersInlineOptions() {
List<String> values = Arrays.asList("1234123412341234", "4321432143214321");
@@ -182,6 +182,30 @@ class HalFormsTemplateBuilderUnitTest {
});
}
@Test // #1510
void propagatesSelectedValueToProperty() {
String selected = "1234123412341234";
List<String> values = Arrays.asList(selected, "4321432143214321");
HalFormsConfiguration configuration = new HalFormsConfiguration()
.withOptions(PatternExample.class, "number",
metadata -> HalFormsOptions.inline(values).withSelectedValue(selected));
RepresentationModel<?> models = new RepresentationModel<>(
Affordances.of(Link.of("/example", LinkRelation.of("create")))
.afford(HttpMethod.POST)
.withInput(PatternExample.class)
.toLink());
Map<String, HalFormsTemplate> templates = new HalFormsTemplateBuilder(configuration, MessageResolver.DEFAULTS_ONLY)
.findTemplates(models);
assertThat(templates.get("default").getPropertyByName("number")).hasValueSatisfying(it -> {
assertThat(it.getValue()).isEqualTo(selected);
});
}
@Getter
static class PatternExample extends RepresentationModel<PatternExample> {