#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.
This commit is contained in:
Oliver Drotbohm
2021-01-21 17:39:51 +01:00
parent 8557e69fdb
commit 2be404ef04
6 changed files with 49 additions and 66 deletions

View File

@@ -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<QueryParameter> 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());
}
}

View File

@@ -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<QueryParameter> parameters, PayloadMetadata outputType) {
return new HalFormsAffordanceModel(name, link, httpMethod, inputType, parameters, outputType);
public AffordanceModel getAffordanceModel(ConfiguredAffordance configured) {
return new HalFormsAffordanceModel(configured);
}
public MediaType getMediaType() {

View File

@@ -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<HalFormsProperty> properties;
private List<MediaType> 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<HalFormsProperty> properties,
List<MediaType> 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<HalFormsProperty> 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<HalFormsProperty> 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<MediaType> contentTypes = new ArrayList<>(this.contentTypes);
contentTypes.add(mediaType);
return new HalFormsTemplate(title, httpMethod, properties, contentTypes, target);
}
HalFormsTemplate withContentTypes(List<MediaType> 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<MediaType> mediaTypes) {
this.contentTypes = mediaTypes;
void setContentType(MediaType mediaType) {
this.contentType = mediaType;
}
@Nullable
@@ -173,10 +152,6 @@ final class HalFormsTemplate {
return this.properties;
}
List<MediaType> 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 + ")";
}
}

View File

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

View File

@@ -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() //

View File

@@ -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<String, HalFormsTemplate> 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<PatternExample> {