Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b549631

Browse files
committedJun 7, 2018
DATAREST-1205 - Use Affordances API
Leverage the Affordances API while making other media types work.
1 parent 6986a35 commit b549631

File tree

21 files changed

+588
-54
lines changed

21 files changed

+588
-54
lines changed
 

‎spring-data-rest-core/pom.xml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
</parent>
1717

1818
<properties>
19-
<springplugin>1.2.0.RELEASE</springplugin>
19+
<springplugin>2.0.0.BUILD-SNAPSHOT</springplugin>
2020
<evoinflector>1.2.2</evoinflector>
21+
<spring-hateoas>1.0.0.SDR-SNAPSHOT</spring-hateoas>
2122
<java-module-name>spring.data.rest.core</java-module-name>
2223
<project.root>${basedir}/..</project.root>
2324
</properties>

‎spring-data-rest-core/src/main/java/org/springframework/data/rest/core/util/Java8PluginRegistry.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public static <T extends Plugin<S>, S> Java8PluginRegistry<T, S> empty() {
4646
}
4747

4848
public Optional<T> getPluginFor(S delimiter) {
49-
return Optional.ofNullable(registry.getPluginFor(delimiter));
49+
return registry.getPluginFor(delimiter);
5050
}
5151

5252
public T getPluginOrDefaultFor(S delimiter, T fallback) {

‎spring-data-rest-tests/spring-data-rest-tests-core/src/test/java/org/springframework/data/rest/tests/ResourceTester.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public Link assertHasLinkEndingWith(String rel, String hrefEnd) {
8383

8484
private final Link assertHasLinkMatching(String rel, Matcher<String> hrefMatcher) {
8585

86-
Link link = resource.getLink(rel);
86+
Link link = resource.getRequiredLink(rel);
8787
assertThat("Expected link with rel '" + rel + "' but didn't find it in " + resource.getLinks(), link,
8888
is(notNullValue()));
8989

‎spring-data-rest-tests/spring-data-rest-tests-jpa/pom.xml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@
4747
<scope>test</scope>
4848
</dependency>
4949

50+
<dependency>
51+
<groupId>org.springframework</groupId>
52+
<artifactId>spring-web</artifactId>
53+
<version>${spring}</version>
54+
</dependency>
55+
56+
<dependency>
57+
<groupId>org.springframework.hateoas</groupId>
58+
<artifactId>spring-hateoas</artifactId>
59+
<version>1.0.0.SDR-SNAPSHOT</version>
60+
<scope>test</scope>
61+
</dependency>
62+
5063
<!-- Jackson Hibernate -->
5164

5265
<dependency>

‎spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/AffordanceIntegrationTests.java‎

Lines changed: 271 additions & 0 deletions
Large diffs are not rendered by default.

‎spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinksIntegrationTests.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public void returnsLinksToSearchResources() {
9999

100100
assertThat(links.hasLink("firstname")).isTrue();
101101

102-
Link firstnameLink = links.getLink("firstname");
102+
Link firstnameLink = links.getLink("firstname").orElse(null);
103103
assertThat(firstnameLink.isTemplated()).isTrue();
104104
assertThat(firstnameLink.getVariableNames()).contains("page", "size");
105105
}

‎spring-data-rest-tests/spring-data-rest-tests-mongodb/src/test/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssemblerIntegrationTests.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public void addsSelfAndSingleResourceLinkToResourceByDefault() throws Exception
7272
Links links = new Links(resource.getLinks());
7373

7474
assertThat(links).hasSize(2);
75-
assertThat(links.getLink("self").getVariables()).isEmpty();
76-
assertThat(links.getLink("user").getVariableNames()).contains("projection");
75+
assertThat(links.getLink("self").orElseThrow(() -> new RuntimeException("Unable to find 'self' link")).getVariables()).isEmpty();
76+
assertThat(links.getLink("user").orElseThrow(() -> new RuntimeException("Unable to find 'user' link")).getVariableNames()).contains("projection");
7777
}
7878
}

‎spring-data-rest-webmvc/pom.xml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@
124124
<scope>test</scope>
125125
</dependency>
126126

127+
<dependency>
128+
<groupId>org.springframework.data</groupId>
129+
<artifactId>spring-data-jpa</artifactId>
130+
<version>${springdata.jpa}</version>
131+
<scope>test</scope>
132+
</dependency>
133+
127134
</dependencies>
128135

129136
</project>

‎spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected Link resourceLink(RootResourceInformation resourceLink, Resource resou
6363

6464
ResourceMetadata repoMapping = resourceLink.getResourceMetadata();
6565

66-
Link selfLink = resource.getLink("self");
66+
Link selfLink = resource.getRequiredLink(Link.REL_SELF);
6767
String rel = repoMapping.getItemResourceRel();
6868

6969
return new Link(selfLink.getHref(), rel);

‎spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@
1515
*/
1616
package org.springframework.data.rest.webmvc;
1717

18+
import static org.springframework.hateoas.core.DummyInvocationUtils.*;
19+
1820
import lombok.NonNull;
1921
import lombok.RequiredArgsConstructor;
22+
import lombok.Value;
23+
24+
import java.lang.reflect.Method;
2025

2126
import org.springframework.data.mapping.PersistentEntity;
2227
import org.springframework.data.mapping.context.PersistentEntities;
@@ -26,9 +31,14 @@
2631
import org.springframework.data.rest.webmvc.support.Projector;
2732
import org.springframework.hateoas.Link;
2833
import org.springframework.hateoas.ResourceAssembler;
34+
import org.springframework.hateoas.collectionjson.CollectionJsonAffordanceModel;
2935
import org.springframework.hateoas.core.EmbeddedWrapper;
3036
import org.springframework.hateoas.core.EmbeddedWrappers;
37+
import org.springframework.hateoas.hal.forms.HalFormsAffordanceModel;
38+
import org.springframework.http.HttpMethod;
3139
import org.springframework.util.Assert;
40+
import org.springframework.web.util.UriComponents;
41+
import org.springframework.web.util.UriComponentsBuilder;
3242

3343
/**
3444
* {@link ResourceAssembler} to create {@link PersistentEntityResource}s for arbitrary domain objects.
@@ -71,10 +81,22 @@ private Builder wrap(Object instance, Object source) {
7181

7282
PersistentEntity<?, ?> entity = entities.getRequiredPersistentEntity(source.getClass());
7383

84+
Link selfLink = getSelfLinkFor(source);
85+
86+
SpringDataRestAffordance putItemAffordance = new SpringDataRestAffordance(HttpMethod.PUT, source.getClass(),"put" + source.getClass().getSimpleName());
87+
putItemAffordance.addAffordanceModel(new HalFormsAffordanceModel(putItemAffordance, selfLink));
88+
putItemAffordance.addAffordanceModel(new CollectionJsonAffordanceModel(putItemAffordance, selfLink));
89+
90+
SpringDataRestAffordance patchItemAffordance = new SpringDataRestAffordance(HttpMethod.PATCH, source.getClass(), "patch" + source.getClass().getSimpleName());
91+
patchItemAffordance.addAffordanceModel(new HalFormsAffordanceModel(patchItemAffordance, selfLink));
92+
patchItemAffordance.addAffordanceModel(new CollectionJsonAffordanceModel(patchItemAffordance, selfLink));
93+
7494
return PersistentEntityResource.build(instance, entity).//
75-
withEmbedded(getEmbeddedResources(source)).//
76-
withLink(getSelfLinkFor(source)).//
77-
withLink(linkProvider.createSelfLinkFor(source));
95+
withEmbedded(getEmbeddedResources(source)).//
96+
withLink(selfLink
97+
.andAffordance(putItemAffordance)
98+
.andAffordance(patchItemAffordance)).//
99+
withLink(linkProvider.createSelfLinkFor(source));
78100
}
79101

80102
/**

‎spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.data.rest.core.mapping.ResourceMetadata;
2424
import org.springframework.data.web.PagedResourcesAssembler;
2525
import org.springframework.hateoas.EntityLinks;
26+
import org.springframework.hateoas.Link;
2627
import org.springframework.http.HttpEntity;
2728
import org.springframework.http.HttpHeaders;
2829
import org.springframework.http.HttpMethod;
@@ -105,6 +106,8 @@ public HttpEntity<RepositoryLinksResource> listRepositories() {
105106

106107
RepositoryLinksResource resource = new RepositoryLinksResource();
107108

109+
resource.add(new Link("/"));
110+
108111
for (Class<?> domainType : repositories) {
109112

110113
ResourceMetadata metadata = mappings.getMetadataFor(domainType);
@@ -113,6 +116,6 @@ public HttpEntity<RepositoryLinksResource> listRepositories() {
113116
}
114117
}
115118

116-
return new ResponseEntity<RepositoryLinksResource>(resource, HttpStatus.OK);
119+
return new ResponseEntity<>(resource, HttpStatus.OK);
117120
}
118121
}

‎spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Collections;
2424
import java.util.List;
2525
import java.util.Optional;
26+
import java.util.stream.Collectors;
2627

2728
import org.springframework.beans.factory.annotation.Autowired;
2829
import org.springframework.context.ApplicationEventPublisher;
@@ -58,6 +59,8 @@
5859
import org.springframework.hateoas.ResourceSupport;
5960
import org.springframework.hateoas.Resources;
6061
import org.springframework.hateoas.UriTemplate;
62+
import org.springframework.hateoas.collectionjson.CollectionJsonAffordanceModel;
63+
import org.springframework.hateoas.hal.forms.HalFormsAffordanceModel;
6164
import org.springframework.http.HttpHeaders;
6265
import org.springframework.http.HttpMethod;
6366
import org.springframework.http.HttpStatus;
@@ -208,7 +211,32 @@ public Resources<?> getCollectionResource(@QuerydslPredicate RootResourceInforma
208211

209212
Resources<?> result = toResources(results, assembler, metadata.getDomainType(), baseLink);
210213
result.add(getCollectionResourceLinks(resourceInformation, pageable));
211-
return result;
214+
return addAffordances(metadata.getDomainType(), result);
215+
}
216+
217+
private Resources<?> addAffordances(Class<?> domainType, Resources<?> resources) {
218+
219+
if (resources instanceof PagedResources) {
220+
return new PagedResources<>(resources.getContent(), ((PagedResources<?>) resources).getMetadata(), addAffordancesToLinks(domainType, resources.getLinks()));
221+
}
222+
223+
return new Resources<>(resources.getContent(), addAffordancesToLinks(domainType, resources.getLinks()));
224+
}
225+
226+
private List<Link> addAffordancesToLinks(Class<?> domainType, List<Link> links) {
227+
228+
return links.stream()
229+
.map(link -> {
230+
if (link.hasRel(Link.REL_SELF)) {
231+
SpringDataRestAffordance postItemAffordance = new SpringDataRestAffordance(HttpMethod.POST, domainType, "post" + domainType.getSimpleName());
232+
postItemAffordance.addAffordanceModel(new HalFormsAffordanceModel(postItemAffordance, link));
233+
postItemAffordance.addAffordanceModel(new CollectionJsonAffordanceModel(postItemAffordance, link));
234+
return link.andAffordance(postItemAffordance);
235+
} else {
236+
return link;
237+
}
238+
})
239+
.collect(Collectors.toList());
212240
}
213241

214242
private List<Link> getCollectionResourceLinks(RootResourceInformation resourceInformation,

‎spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryPropertyReferenceController.java‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,11 @@ public ResponseEntity<ResourceSupport> followPropertyReference(final RootResourc
134134
} else {
135135

136136
PersistentEntityResource resource = assembler.toResource(it);
137-
headers.set("Content-Location", resource.getId().getHref());
137+
headers.set("Content-Location", resource.getRequiredLink(Link.REL_SELF).getHref());
138138
return resource;
139139
}
140140

141-
}).orElseThrow(() -> new ResourceNotFoundException());
141+
}).orElseThrow(ResourceNotFoundException::new);
142142

143143
return ControllerUtils.toResponseEntity(HttpStatus.OK, headers, //
144144
doWithReferencedProperty(repoRequest, id, property, handler, HttpMethod.GET));
@@ -187,7 +187,7 @@ public ResponseEntity<ResourceSupport> followPropertyReference(RootResourceInfor
187187
if (propertyId.equals(accessor1.getIdentifier().toString())) {
188188

189189
PersistentEntityResource resource1 = assembler.toResource(obj);
190-
headers.set("Content-Location", resource1.getId().getHref());
190+
headers.set("Content-Location", resource1.getRequiredLink(Link.REL_SELF).getHref());
191191
return resource1;
192192
}
193193
}
@@ -200,18 +200,18 @@ public ResponseEntity<ResourceSupport> followPropertyReference(RootResourceInfor
200200
if (propertyId.equals(accessor2.getIdentifier().toString())) {
201201

202202
PersistentEntityResource resource2 = assembler.toResource(entry.getValue());
203-
headers.set("Content-Location", resource2.getId().getHref());
203+
headers.set("Content-Location", resource2.getRequiredLink(Link.REL_SELF).getHref());
204204
return resource2;
205205
}
206206
}
207207

208208
} else {
209-
return new Resource<Object>(prop.propertyValue);
209+
return new Resource<>(prop.propertyValue);
210210
}
211211

212212
throw new ResourceNotFoundException();
213213

214-
}).orElseThrow(() -> new ResourceNotFoundException());
214+
}).orElseThrow(ResourceNotFoundException::new);
215215

216216
return ControllerUtils.toResponseEntity(HttpStatus.OK, headers, //
217217
doWithReferencedProperty(repoRequest, id, property, handler, HttpMethod.GET));
@@ -254,7 +254,7 @@ public ResponseEntity<ResourceSupport> followPropertyReferenceCompact(RootResour
254254
Map<Object, Resource<?>> map = (Map<Object, Resource<?>>) content;
255255

256256
for (Entry<Object, Resource<?>> entry : map.entrySet()) {
257-
Link l = new Link(entry.getValue().getLink("self").getHref(), entry.getKey().toString());
257+
Link l = new Link(entry.getValue().getRequiredLink(Link.REL_SELF).getHref(), entry.getKey().toString());
258258
links.add(l);
259259
}
260260
}

‎spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.data.rest.core.mapping.ResourceMappings;
3636
import org.springframework.data.rest.core.mapping.ResourceMetadata;
3737
import org.springframework.data.rest.webmvc.support.JpaHelper;
38+
import org.springframework.hateoas.MediaTypes;
3839
import org.springframework.http.HttpMethod;
3940
import org.springframework.http.MediaType;
4041
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
@@ -203,6 +204,8 @@ protected ProducesRequestCondition customize(ProducesRequestCondition condition)
203204
HashSet<String> mediaTypes = new LinkedHashSet<String>();
204205
mediaTypes.add(configuration.getDefaultMediaType().toString());
205206
mediaTypes.add(MediaType.APPLICATION_JSON_VALUE);
207+
mediaTypes.add(MediaTypes.HAL_FORMS_JSON_VALUE);
208+
mediaTypes.add(MediaTypes.COLLECTION_JSON_VALUE);
206209

207210
return new ProducesRequestCondition(mediaTypes.toArray(new String[mediaTypes.size()]));
208211
}

‎spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java‎

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -344,23 +344,20 @@ private Optional<Object> executeQueryMethod(final RepositoryInvoker invoker,
344344
List<TypeInformation<?>> parameterTypeInformations = ClassTypeInformation.from(method.getDeclaringClass())
345345
.getParameterTypes(method);
346346

347-
for (Entry<String, List<Object>> entry : parameters.entrySet()) {
347+
parameters.entrySet().forEach(entry ->
348348

349-
MethodParameter parameter = methodParameters.getParameter(entry.getKey());
349+
methodParameters.getParameter(entry.getKey()).ifPresent(parameter -> {
350350

351-
if (parameter == null) {
352-
continue;
353-
}
354-
355-
int parameterIndex = parameterList.indexOf(parameter);
356-
TypeInformation<?> domainType = parameterTypeInformations.get(parameterIndex).getActualType();
351+
int parameterIndex = parameterList.indexOf(parameter);
352+
TypeInformation<?> domainType = parameterTypeInformations.get(parameterIndex).getActualType();
357353

358-
ResourceMetadata metadata = mappings.getMetadataFor(domainType.getType());
354+
ResourceMetadata metadata = mappings.getMetadataFor(domainType.getType());
359355

360-
if (metadata != null && metadata.isExported()) {
361-
result.put(parameter.getParameterName(), prepareUris(entry.getValue()));
356+
if (metadata != null && metadata.isExported()) {
357+
result.put(parameter.getParameterName(), prepareUris(entry.getValue()));
358+
}
362359
}
363-
}
360+
));
364361

365362
return invoker.invokeQueryMethod(method, result, pageable.getPageable(), sort);
366363
}

0 commit comments

Comments
 (0)
Please sign in to comment.