Skip to content

Commit

Permalink
#1510 - Support for defining the selected value on HalFormsOptions.
Browse files Browse the repository at this point in the history
We now expose a HalFormsOptions.withSelectedValue(…) and propagate this into the HAL FORMS property field named "value".
  • Loading branch information
odrotbohm committed Apr 8, 2021
1 parent 3977514 commit bbb8f42
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand Down Expand Up @@ -61,7 +62,7 @@ public static Inline inline(Collection<? extends Object> values) {

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);
}

/**
Expand All @@ -74,7 +75,7 @@ public static Remote remote(Link link) {

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);
}

/**
Expand Down Expand Up @@ -122,21 +123,26 @@ public static Remote remote(String href) {
@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!");

this.promptField = promptRef;
this.valueField = valueRef;
this.minItems = minItems;
this.maxItems = maxItems;
this.selectedValue = selectedValue;
}

/*
Expand Down Expand Up @@ -179,6 +185,17 @@ public Long getMaxItems() {
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.
*
Expand All @@ -191,7 +208,7 @@ public T withPromptField(String promptField) {
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);
}

/**
Expand All @@ -206,7 +223,7 @@ public T withValueField(String valueField) {
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);
}

/**
Expand All @@ -221,7 +238,7 @@ public T withMinItems(Long minItems) {
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);
}

/**
Expand All @@ -230,13 +247,23 @@ public T withMinItems(Long minItems) {
* @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);
}

/**
Expand All @@ -249,7 +276,7 @@ public T withMaxItems(Long maxItems) {
* @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> {
Expand All @@ -262,9 +289,9 @@ public static class Inline extends AbstractHalFormsOptions<Inline> {
* @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!");

Expand All @@ -283,12 +310,12 @@ public Collection<? extends Object> getInline() {

/*
* (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);
}
}

Expand All @@ -302,9 +329,9 @@ public static class Remote extends AbstractHalFormsOptions<Remote> {
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!");

Expand All @@ -323,12 +350,12 @@ public Link getLink() {

/*
* (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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
@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;
Expand All @@ -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;
Expand Down Expand Up @@ -132,7 +134,7 @@ HalFormsProperty withReadOnly(boolean readOnly) {
* @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,
Expand Down Expand Up @@ -329,7 +331,7 @@ boolean isReadOnly() {
}

@JsonProperty
String getValue() {
Object getValue() {
return this.value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ public List<HalFormsProperty> createProperties(HalFormsAffordanceModel model) {
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())
Expand All @@ -97,7 +98,8 @@ public List<HalFormsProperty> createProperties(HalFormsAffordanceModel model) {
.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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ void exposesInputMediaTypeAsContentType() {
}

@Test // #1483
void rendersRegisteredSuggest() {
void rendersInlineOptions() {

List<String> values = Arrays.asList("1234123412341234", "4321432143214321");

Expand All @@ -182,6 +182,30 @@ void rendersRegisteredSuggest() {
});
}

@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> {

Expand Down

0 comments on commit bbb8f42

Please sign in to comment.