Skip to content

Commit a597960

Browse files
committed
#416 - Alter ResourceAssemblerSupport.toResources to return Resources.
To avoid confusion about what to return on a Spring MVC endpoint, change toResources to not return `List<D>`, but instead `Resources<D>`. This clearly ensures when used to construct Spring MVC endpoints, will return a type Spring HATEOAS will properly marshal. To support people that have already built apps on top of Lists, include a `toList<D>` method that honors the old contract. Related issues: #493 Related pull requests: #572
1 parent c333259 commit a597960

File tree

4 files changed

+145
-28
lines changed

4 files changed

+145
-28
lines changed

src/main/java/org/springframework/hateoas/ResourceAssembler.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
*/
1616
package org.springframework.hateoas;
1717

18+
import java.util.ArrayList;
19+
import java.util.List;
20+
1821
/**
1922
* Interface for components that convert a domain type into an {@link ResourceSupport}.
2023
*
2124
* @author Oliver Gierke
25+
* @author Greg Turnquist
2226
*/
2327
public interface ResourceAssembler<T, D extends ResourceSupport> {
2428

@@ -29,4 +33,20 @@ public interface ResourceAssembler<T, D extends ResourceSupport> {
2933
* @return
3034
*/
3135
D toResource(T entity);
36+
37+
/**
38+
* Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link ResourceSupport} and wraps
39+
* them in a {@link Resources} instance.
40+
*
41+
* @param entities
42+
* @return
43+
*/
44+
default Resources<D> toResources(List<T> entities) {
45+
46+
List<D> resources = new ArrayList<>();
47+
for (T entity : entities) {
48+
resources.add(toResource(entity));
49+
}
50+
return new Resources<>(resources);
51+
}
3252
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.hateoas.core;
17+
18+
/**
19+
* Utilities to mimic {@link java.util.Objects} helper methods.
20+
*
21+
* @author Greg Turnquist
22+
*/
23+
public final class Objects {
24+
25+
/**
26+
* Variant of {@link java.util.Objects#requireNonNull(Object, String)} that throws an {@link IllegalArgumentException}.
27+
*/
28+
public static <T> T requireNonNull(T obj, String message) {
29+
30+
if (obj == null) {
31+
throw new IllegalArgumentException(message);
32+
}
33+
return obj;
34+
}
35+
}

src/main/java/org/springframework/hateoas/mvc/ResourceAssemblerSupport.java

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2013 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,13 +23,15 @@
2323
import org.springframework.beans.BeanUtils;
2424
import org.springframework.hateoas.ResourceAssembler;
2525
import org.springframework.hateoas.ResourceSupport;
26-
import org.springframework.util.Assert;
26+
import org.springframework.hateoas.Resources;
27+
import org.springframework.hateoas.core.Objects;
2728

2829
/**
2930
* Base class to implement {@link ResourceAssembler}s. Will automate {@link ResourceSupport} instance creation and make
3031
* sure a self-link is always added.
3132
*
3233
* @author Oliver Gierke
34+
* @author Greg Turnquist
3335
*/
3436
public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> implements ResourceAssembler<T, D> {
3537

@@ -44,30 +46,20 @@ public abstract class ResourceAssemblerSupport<T, D extends ResourceSupport> imp
4446
*/
4547
public ResourceAssemblerSupport(Class<?> controllerClass, Class<D> resourceType) {
4648

47-
Assert.notNull(controllerClass, "ControllerClass must not be null!");
48-
Assert.notNull(resourceType, "ResourceType must not be null!");
49+
Objects.requireNonNull(controllerClass, "ControllerClass must not be null!");
50+
Objects.requireNonNull(resourceType, "ResourceType must not be null!");
4951

5052
this.controllerClass = controllerClass;
5153
this.resourceType = resourceType;
5254
}
5355

54-
/**
55-
* Converts all given entities into resources.
56-
*
57-
* @see #toResource(Object)
58-
* @param entities must not be {@literal null}.
59-
* @return
60-
*/
61-
public List<D> toResources(Iterable<? extends T> entities) {
62-
63-
Assert.notNull(entities, "Entities must not be null!");
64-
List<D> result = new ArrayList<D>();
65-
66-
for (T entity : entities) {
67-
result.add(toResource(entity));
68-
}
56+
@Override
57+
public Resources<D> toResources(List<T> entities) {
58+
return this.map(entities).toResources();
59+
}
6960

70-
return result;
61+
public Builder<T, D> map(Iterable<? extends T> entities) {
62+
return new Builder<>(entities, this);
7163
}
7264

7365
/**
@@ -83,11 +75,11 @@ protected D createResourceWithId(Object id, T entity) {
8375

8476
protected D createResourceWithId(Object id, T entity, Object... parameters) {
8577

86-
Assert.notNull(entity, "Entity must not be null!");
87-
Assert.notNull(id, "Id must not be null!");
78+
Objects.requireNonNull(entity, "Entity must not be null!");
79+
Objects.requireNonNull(id, "Id must not be null!");
8880

8981
D instance = instantiateResource(entity);
90-
instance.add(linkTo(controllerClass, parameters).slash(id).withSelfRel());
82+
instance.add(linkTo(this.controllerClass, parameters).slash(id).withSelfRel());
9183
return instance;
9284
}
9385

@@ -100,6 +92,46 @@ protected D createResourceWithId(Object id, T entity, Object... parameters) {
10092
* @return
10193
*/
10294
protected D instantiateResource(T entity) {
103-
return BeanUtils.instantiateClass(resourceType);
95+
return BeanUtils.instantiateClass(this.resourceType);
96+
}
97+
98+
static class Builder<T, D extends ResourceSupport> {
99+
100+
private final Iterable<? extends T> entities;
101+
private final ResourceAssemblerSupport<T, D> resourceAssembler;
102+
103+
Builder(Iterable<? extends T> entities, ResourceAssemblerSupport<T, D> resourceAssembler) {
104+
105+
this.entities = Objects.requireNonNull(entities, "entities must not null!");
106+
this.resourceAssembler = resourceAssembler;
107+
}
108+
109+
/**
110+
* Transform a list of {@code T}s into a list of {@link ResourceSupport}s.
111+
*
112+
* @see {@link #toListOfResources()} if you need this transformed list rendered as hypermedia
113+
*
114+
* @return
115+
*/
116+
public List<D> toListOfResources() {
117+
118+
List<D> result = new ArrayList<>();
119+
120+
for (T entity : this.entities) {
121+
result.add(this.resourceAssembler.toResource(entity));
122+
}
123+
124+
return result;
125+
}
126+
127+
/**
128+
* Converts all given entities into resources and wraps the result in a {@link Resources} instance.
129+
*
130+
* @see {@link #toListOfResources()}} and {@link ResourceAssembler#toResource(Object)}
131+
* @return
132+
*/
133+
public Resources<D> toResources() {
134+
return new Resources<>(toListOfResources());
135+
}
104136
}
105137
}

src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,13 +28,15 @@
2828
import org.springframework.hateoas.Link;
2929
import org.springframework.hateoas.LinkBuilder;
3030
import org.springframework.hateoas.ResourceSupport;
31+
import org.springframework.hateoas.Resources;
3132
import org.springframework.hateoas.TestUtils;
3233
import org.springframework.web.bind.annotation.RequestMapping;
3334

3435
/**
3536
* Unit tests for {@link IdentifiableResourceAssemblerSupport}.
3637
*
3738
* @author Oliver Gierke
39+
* @author Greg Turnquist
3840
*/
3941
public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils {
4042

@@ -81,6 +83,34 @@ public void unwrapsIdentifyablesForParameters() {
8183
.hasValueSatisfying(it -> assertThat(it.endsWith("/people/id")));
8284
}
8385

86+
/**
87+
* @see #416
88+
*/
89+
@Test
90+
public void convertsEntitiesToListOfResources() {
91+
92+
Person first = new Person();
93+
first.id = 1L;
94+
Person second = new Person();
95+
second.id = 2L;
96+
97+
List<PersonResource> result = assembler.map(Arrays.asList(first, second)).toListOfResources();
98+
99+
LinkBuilder builder = linkTo(PersonController.class);
100+
101+
PersonResource firstResource = new PersonResource();
102+
firstResource.add(builder.slash(1L).withSelfRel());
103+
104+
PersonResource secondResource = new PersonResource();
105+
secondResource.add(builder.slash(1L).withSelfRel());
106+
107+
assertThat(result).hasSize(2);
108+
assertThat(result).contains(firstResource, secondResource);
109+
}
110+
111+
/**
112+
* @see #416
113+
*/
84114
@Test
85115
public void convertsEntitiesToResources() {
86116

@@ -89,7 +119,7 @@ public void convertsEntitiesToResources() {
89119
Person second = new Person();
90120
second.id = 2L;
91121

92-
List<PersonResource> result = assembler.toResources(Arrays.asList(first, second));
122+
Resources<PersonResource> result = assembler.map(Arrays.asList(first, second)).toResources();
93123

94124
LinkBuilder builder = linkTo(PersonController.class);
95125

@@ -130,11 +160,11 @@ static class PersonResource extends ResourceSupport {
130160

131161
class PersonResourceAssembler extends IdentifiableResourceAssemblerSupport<Person, PersonResource> {
132162

133-
public PersonResourceAssembler() {
163+
PersonResourceAssembler() {
134164
this(PersonController.class);
135165
}
136166

137-
public PersonResourceAssembler(Class<?> controllerType) {
167+
PersonResourceAssembler(Class<?> controllerType) {
138168
super(controllerType, PersonResource.class);
139169
}
140170

0 commit comments

Comments
 (0)