From 2be404ef04f1594c6446b67e2ad1e03da5fbe765 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 21 Jan 2021 17:39:51 +0100 Subject: [PATCH] #1443 - Expose affordance input media type in HAL FORMS contentType attribute. Fixed the contentType in HalFormsTemplate to only contain a single media type. Also adapted HalFormsAffordanceModelFactory to the new API introduced for #1441. --- .../hal/forms/HalFormsAffordanceModel.java | 12 ++-- .../forms/HalFormsAffordanceModelFactory.java | 15 ++--- .../mediatype/hal/forms/HalFormsTemplate.java | 63 ++++++------------- .../hal/forms/HalFormsTemplateBuilder.java | 4 +- .../HalFormsMessageConverterUnitTest.java | 2 +- .../HalFormsTemplateBuilderUnitTest.java | 19 ++++++ 6 files changed, 49 insertions(+), 66 deletions(-) diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java index ad889664..4324b742 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModel.java @@ -15,12 +15,8 @@ */ package org.springframework.hateoas.mediatype.hal.forms; -import java.util.List; - import org.springframework.hateoas.AffordanceModel; -import org.springframework.hateoas.Link; -import org.springframework.hateoas.QueryParameter; -import org.springframework.http.HttpMethod; +import org.springframework.hateoas.mediatype.ConfiguredAffordance; import org.springframework.http.MediaType; /** @@ -31,8 +27,8 @@ import org.springframework.http.MediaType; */ class HalFormsAffordanceModel extends AffordanceModel { - public HalFormsAffordanceModel(String name, Link link, HttpMethod httpMethod, InputPayloadMetadata inputType, - List queryMethodParameters, PayloadMetadata outputType) { - super(name, link, httpMethod, inputType, queryMethodParameters, outputType); + public HalFormsAffordanceModel(ConfiguredAffordance configured) { + super(configured.getNameOrDefault(), configured.getTarget(), configured.getMethod(), configured.getInputMetadata(), + configured.getQueryParameters(), configured.getOutputMetadata()); } } diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModelFactory.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModelFactory.java index a8cc175d..46aed3e3 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModelFactory.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsAffordanceModelFactory.java @@ -15,16 +15,10 @@ */ package org.springframework.hateoas.mediatype.hal.forms; -import java.util.List; - import org.springframework.hateoas.AffordanceModel; -import org.springframework.hateoas.AffordanceModel.InputPayloadMetadata; -import org.springframework.hateoas.AffordanceModel.PayloadMetadata; -import org.springframework.hateoas.Link; import org.springframework.hateoas.MediaTypes; -import org.springframework.hateoas.QueryParameter; import org.springframework.hateoas.mediatype.AffordanceModelFactory; -import org.springframework.http.HttpMethod; +import org.springframework.hateoas.mediatype.ConfiguredAffordance; import org.springframework.http.MediaType; /** @@ -39,12 +33,11 @@ class HalFormsAffordanceModelFactory implements AffordanceModelFactory { /* * (non-Javadoc) - * @see org.springframework.hateoas.AffordanceModelFactory#getAffordanceModel(java.lang.String, org.springframework.hateoas.Link, org.springframework.http.HttpMethod, org.springframework.core.ResolvableType, java.util.List, org.springframework.core.ResolvableType) + * @see org.springframework.hateoas.mediatype.AffordanceModelFactory#getAffordanceModel(org.springframework.hateoas.mediatype.ConfiguredAffordance) */ @Override - public AffordanceModel getAffordanceModel(String name, Link link, HttpMethod httpMethod, - InputPayloadMetadata inputType, List parameters, PayloadMetadata outputType) { - return new HalFormsAffordanceModel(name, link, httpMethod, inputType, parameters, outputType); + public AffordanceModel getAffordanceModel(ConfiguredAffordance configured) { + return new HalFormsAffordanceModel(configured); } public MediaType getMediaType() { 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 3388432a..72eca88b 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 @@ -21,12 +21,10 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import org.springframework.hateoas.mediatype.hal.forms.HalFormsDeserializers.MediaTypesDeserializer; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; @@ -34,7 +32,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; /** * Value object for a HAL-FORMS template. Describes the available state transition details. @@ -53,21 +50,21 @@ final class HalFormsTemplate { private String title; private HttpMethod httpMethod; private List properties; - private List contentTypes; + private MediaType contentType; private String target; @SuppressWarnings("null") private HalFormsTemplate() { - this(null, null, Collections.emptyList(), Collections.emptyList(), null); + this(null, null, Collections.emptyList(), null, null); } private HalFormsTemplate(String title, HttpMethod httpMethod, List properties, - List contentTypes, String target) { + @Nullable MediaType contentType, String target) { this.title = title; this.httpMethod = httpMethod; this.properties = properties; - this.contentTypes = contentTypes; + this.contentType = contentType; this.target = target; } @@ -78,13 +75,13 @@ final class HalFormsTemplate { HalFormsTemplate withTitle(String title) { return this.title == title ? this - : new HalFormsTemplate(title, this.httpMethod, this.properties, this.contentTypes, this.target); + : new HalFormsTemplate(title, this.httpMethod, this.properties, this.contentType, this.target); } private HalFormsTemplate withHttpMethod(HttpMethod httpMethod) { return this.httpMethod == httpMethod ? this - : new HalFormsTemplate(this.title, httpMethod, this.properties, this.contentTypes, this.target); + : new HalFormsTemplate(this.title, httpMethod, this.properties, this.contentType, this.target); } /** @@ -100,53 +97,35 @@ final class HalFormsTemplate { List properties = new ArrayList<>(this.properties); properties.add(property); - return new HalFormsTemplate(title, httpMethod, properties, contentTypes, target); + return new HalFormsTemplate(title, httpMethod, properties, contentType, target); } HalFormsTemplate withProperties(List properties) { return this.properties == properties ? this - : new HalFormsTemplate(title, httpMethod, properties, contentTypes, target); + : new HalFormsTemplate(title, httpMethod, properties, contentType, target); } - /** - * Returns a new {@link HalFormsTemplate} with the given {@link MediaType} added as content type. - * - * @param mediaType must not be {@literal null}. - * @return - */ - HalFormsTemplate andContentType(MediaType mediaType) { + HalFormsTemplate withContentType(@Nullable MediaType contentType) { - Assert.notNull(mediaType, "Media type must not be null!"); - - List contentTypes = new ArrayList<>(this.contentTypes); - contentTypes.add(mediaType); - - return new HalFormsTemplate(title, httpMethod, properties, contentTypes, target); - } - - HalFormsTemplate withContentTypes(List contentTypes) { - - return this.contentTypes == contentTypes ? this - : new HalFormsTemplate(title, httpMethod, properties, contentTypes, target); + return this.contentType == contentType ? this + : new HalFormsTemplate(title, httpMethod, properties, contentType, target); } HalFormsTemplate withTarget(String target) { return this.target == target ? this - : new HalFormsTemplate(title, httpMethod, properties, contentTypes, target); + : new HalFormsTemplate(title, httpMethod, properties, contentType, target); } - // Jackson helper methods to create the right representation format - + @Nullable @JsonInclude(Include.NON_EMPTY) String getContentType() { - return StringUtils.collectionToDelimitedString(contentTypes, ", "); + return contentType == null ? null : contentType.toString(); } - @JsonDeserialize(using = MediaTypesDeserializer.class) - void setContentType(List mediaTypes) { - this.contentTypes = mediaTypes; + void setContentType(MediaType mediaType) { + this.contentType = mediaType; } @Nullable @@ -173,10 +152,6 @@ final class HalFormsTemplate { return this.properties; } - List getContentTypes() { - return this.contentTypes; - } - @JsonInclude(Include.NON_EMPTY) String getTitle() { return this.title; @@ -207,7 +182,7 @@ final class HalFormsTemplate { return Objects.equals(this.title, that.title) // && this.httpMethod == that.httpMethod // && Objects.equals(this.properties, that.properties) // - && Objects.equals(this.contentTypes, that.contentTypes); + && Objects.equals(this.contentType, that.contentType); } /* @@ -216,7 +191,7 @@ final class HalFormsTemplate { */ @Override public int hashCode() { - return Objects.hash(this.title, this.httpMethod, this.properties, this.contentTypes); + return Objects.hash(this.title, this.httpMethod, this.properties, this.contentType); } /* @@ -226,6 +201,6 @@ final class HalFormsTemplate { @Override public String toString() { return "HalFormsTemplate(title=" + this.title + ", httpMethod=" + this.httpMethod + ", properties=" - + this.properties + ", contentTypes=" + this.contentTypes + ")"; + + this.properties + ", contentTypes=" + this.contentType + ")"; } } 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 a58932a4..89764293 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 @@ -67,7 +67,8 @@ class HalFormsTemplateBuilder { .forEach(it -> { HalFormsTemplate template = HalFormsTemplate.forMethod(it.getHttpMethod()) // - .withProperties(factory.createProperties(it)); + .withProperties(factory.createProperties(it)) + .withContentType(it.getInput().getPrimaryMediaType()); String target = it.getLink().getHref(); @@ -80,7 +81,6 @@ class HalFormsTemplateBuilder { }); return templates; - } private HalFormsTemplate applyTo(HalFormsTemplate template, HalFormsTemplateBuilder.TemplateTitle templateTitle) { diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java index d64414d2..2186d6d0 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java @@ -111,7 +111,7 @@ class HalFormsMessageConverterUnitTest { HalFormsTemplate template = HalFormsTemplate.forMethod(HttpMethod.GET) // .withTitle("HAL-FORMS unit test") // - .andContentType(MediaTypes.HAL_JSON) // + .withContentType(MediaTypes.HAL_JSON) // .andProperty(property); // HalFormsDocument expected = HalFormsDocument.empty() // 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 9f9f9161..0ff658e6 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.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.mediatype.Affordances; import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; /** * @author Oliver Drotbohm @@ -131,6 +132,24 @@ class HalFormsTemplateBuilderUnitTest { assertThat(templates.get("default").getTarget()).isEqualTo("/example"); } + @Test // #1443 + void exposesInputMediaTypeAsContentType() { + + MediaType mediaType = MediaType.parseMediaType("text/uri-list"); + + Link link = Affordances.of(Link.of("/example", LinkRelation.of("create"))) // + .afford(HttpMethod.POST) // + .withInput(Payload.class) // + .withInputMediaType(mediaType) // + .withName("create") // + .toLink(); + + Map templates = new HalFormsTemplateBuilder(new HalFormsConfiguration(), + MessageResolver.DEFAULTS_ONLY).findTemplates(new RepresentationModel<>().add(link)); + + assertThat(templates.get("default").getContentType()).isEqualTo(mediaType.toString()); + } + @Getter static class PatternExample extends RepresentationModel {