diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplate.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplate.java index 5953d5a6..3388432a 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplate.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplate.java @@ -54,19 +54,21 @@ final class HalFormsTemplate { private HttpMethod httpMethod; private List properties; private List 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 properties, - List contentTypes) { + List 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 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 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 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 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) diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java index 56b708ff..a58932a4 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java @@ -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 findTemplates(RepresentationModel resource) { - if (!resource.hasLink(IanaLinkRelations.SELF)) { - return Collections.emptyMap(); - } - Map templates = new HashMap<>(); - List 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); } 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 da32aea7..9f9f9161 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 @@ -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 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 {