#1427 - Support for the target attribute of HAL FORMS templates.
We now explicitly set the target attribute of HAL FORMS templates if the target of the affordance does not equal the target of the self link.
This commit is contained in:
@@ -54,19 +54,21 @@ final class HalFormsTemplate {
|
||||
private HttpMethod httpMethod;
|
||||
private List<HalFormsProperty> properties;
|
||||
private List<MediaType> contentTypes;
|
||||
private String target;
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private HalFormsTemplate() {
|
||||
this(null, null, Collections.emptyList(), Collections.emptyList());
|
||||
this(null, null, Collections.emptyList(), Collections.emptyList(), null);
|
||||
}
|
||||
|
||||
private HalFormsTemplate(String title, HttpMethod httpMethod, List<HalFormsProperty> properties,
|
||||
List<MediaType> contentTypes) {
|
||||
List<MediaType> contentTypes, String target) {
|
||||
|
||||
this.title = title;
|
||||
this.httpMethod = httpMethod;
|
||||
this.properties = properties;
|
||||
this.contentTypes = contentTypes;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
static HalFormsTemplate forMethod(HttpMethod httpMethod) {
|
||||
@@ -76,13 +78,13 @@ final class HalFormsTemplate {
|
||||
HalFormsTemplate withTitle(String title) {
|
||||
|
||||
return this.title == title ? this
|
||||
: new HalFormsTemplate(title, this.httpMethod, this.properties, this.contentTypes);
|
||||
: new HalFormsTemplate(title, this.httpMethod, this.properties, this.contentTypes, this.target);
|
||||
}
|
||||
|
||||
private HalFormsTemplate withHttpMethod(HttpMethod httpMethod) {
|
||||
|
||||
return this.httpMethod == httpMethod ? this
|
||||
: new HalFormsTemplate(this.title, httpMethod, this.properties, this.contentTypes);
|
||||
: new HalFormsTemplate(this.title, httpMethod, this.properties, this.contentTypes, this.target);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,13 +100,13 @@ final class HalFormsTemplate {
|
||||
List<HalFormsProperty> properties = new ArrayList<>(this.properties);
|
||||
properties.add(property);
|
||||
|
||||
return new HalFormsTemplate(title, httpMethod, properties, contentTypes);
|
||||
return new HalFormsTemplate(title, httpMethod, properties, contentTypes, target);
|
||||
}
|
||||
|
||||
HalFormsTemplate withProperties(List<HalFormsProperty> properties) {
|
||||
|
||||
return this.properties == properties ? this
|
||||
: new HalFormsTemplate(this.title, this.httpMethod, properties, this.contentTypes);
|
||||
: new HalFormsTemplate(title, httpMethod, properties, contentTypes, target);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,13 +122,19 @@ final class HalFormsTemplate {
|
||||
List<MediaType> contentTypes = new ArrayList<>(this.contentTypes);
|
||||
contentTypes.add(mediaType);
|
||||
|
||||
return new HalFormsTemplate(title, httpMethod, properties, contentTypes);
|
||||
return new HalFormsTemplate(title, httpMethod, properties, contentTypes, target);
|
||||
}
|
||||
|
||||
HalFormsTemplate withContentTypes(List<MediaType> contentTypes) {
|
||||
|
||||
return this.contentTypes == contentTypes ? this
|
||||
: new HalFormsTemplate(this.title, this.httpMethod, this.properties, contentTypes);
|
||||
: new HalFormsTemplate(title, httpMethod, properties, contentTypes, target);
|
||||
}
|
||||
|
||||
HalFormsTemplate withTarget(String target) {
|
||||
|
||||
return this.target == target ? this
|
||||
: new HalFormsTemplate(title, httpMethod, properties, contentTypes, target);
|
||||
}
|
||||
|
||||
// Jackson helper methods to create the right representation format
|
||||
@@ -174,6 +182,11 @@ final class HalFormsTemplate {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package org.springframework.hateoas.mediatype.hal.forms;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
@@ -34,6 +32,7 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
class HalFormsTemplateBuilder {
|
||||
|
||||
@@ -54,16 +53,11 @@ class HalFormsTemplateBuilder {
|
||||
*/
|
||||
public Map<String, HalFormsTemplate> findTemplates(RepresentationModel<?> resource) {
|
||||
|
||||
if (!resource.hasLink(IanaLinkRelations.SELF)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<String, HalFormsTemplate> templates = new HashMap<>();
|
||||
List<Affordance> affordances = resource.getLink(IanaLinkRelations.SELF) //
|
||||
.map(Link::getAffordances) //
|
||||
.orElse(Collections.emptyList());
|
||||
Link selfLink = resource.getLink(IanaLinkRelations.SELF).orElse(null);
|
||||
|
||||
affordances.stream() //
|
||||
resource.getLinks().stream() //
|
||||
.flatMap(it -> it.getAffordances().stream()) //
|
||||
.map(it -> it.getAffordanceModel(MediaTypes.HAL_FORMS_JSON)) //
|
||||
.peek(it -> {
|
||||
Assert.notNull(it, "No HAL Forms affordance model found but expected!");
|
||||
@@ -75,16 +69,24 @@ class HalFormsTemplateBuilder {
|
||||
HalFormsTemplate template = HalFormsTemplate.forMethod(it.getHttpMethod()) //
|
||||
.withProperties(factory.createProperties(it));
|
||||
|
||||
String target = it.getLink().getHref();
|
||||
|
||||
if (selfLink == null || !target.equals(selfLink.getHref())) {
|
||||
template = template.withTarget(target);
|
||||
}
|
||||
|
||||
template = applyTo(template, TemplateTitle.of(it, templates.isEmpty()));
|
||||
templates.put(templates.isEmpty() ? "default" : it.getName(), template);
|
||||
});
|
||||
|
||||
return templates;
|
||||
|
||||
}
|
||||
|
||||
private HalFormsTemplate applyTo(HalFormsTemplate template, HalFormsTemplateBuilder.TemplateTitle templateTitle) {
|
||||
|
||||
return Optional.ofNullable(resolver.resolve(templateTitle)) //
|
||||
.filter(StringUtils::hasText) //
|
||||
.map(template::withTitle) //
|
||||
.orElse(template);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.mediatype.Affordances;
|
||||
import org.springframework.hateoas.mediatype.MessageResolver;
|
||||
@@ -115,6 +116,21 @@ class HalFormsTemplateBuilderUnitTest {
|
||||
assertThat(text).map(HalFormsProperty::getMaxLength).hasValue(5L);
|
||||
}
|
||||
|
||||
@Test // #1427
|
||||
void addsTargetAttributeForLinksNotPointingToSelf() {
|
||||
|
||||
Link link = Affordances.of(Link.of("/example", LinkRelation.of("create"))) //
|
||||
.afford(HttpMethod.POST) //
|
||||
.withInput(Payload.class) //
|
||||
.withName("create")
|
||||
.toLink();
|
||||
|
||||
Map<String, HalFormsTemplate> templates = new HalFormsTemplateBuilder(new HalFormsConfiguration(),
|
||||
MessageResolver.DEFAULTS_ONLY).findTemplates(new RepresentationModel<>().add(link));
|
||||
|
||||
assertThat(templates.get("default").getTarget()).isEqualTo("/example");
|
||||
}
|
||||
|
||||
@Getter
|
||||
static class PatternExample extends RepresentationModel<PatternExample> {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user