Skip to content

Commit 3ca6fa5

Browse files
Jeff Stanoodrotbohm
Jeff Stano
authored andcommitted
#229 - Added support for curies in HalEmbeddedBuilder.
HalEmbeddedBuilder now takes an optional CurieProvider to create namespaced rels for embeddededs. Original pull request: #233.
1 parent 994347c commit 3ca6fa5

File tree

7 files changed

+127
-93
lines changed

7 files changed

+127
-93
lines changed

src/main/java/org/springframework/hateoas/hal/CurieProvider.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*
2626
* @see http://tools.ietf.org/html/draft-kelly-json-hal#section-8.2
2727
* @author Oliver Gierke
28+
* @author Jeff Stano
2829
* @since 0.9
2930
*/
3031
public interface CurieProvider {
@@ -38,6 +39,16 @@ public interface CurieProvider {
3839
*/
3940
String getNamespacedRelFrom(Link link);
4041

42+
/**
43+
* Returns the rel to be rendered for the given rel. Will potentially prefix the rel but also might decide not to,
44+
* depending on the actual rel.
45+
*
46+
* @param rel
47+
* @return
48+
* @since 0.17
49+
*/
50+
String getNamespacedRelFor(String rel);
51+
4152
/**
4253
* Returns an object to render as the base curie information. Implementations have to make sure, the retunred
4354
* instances renders as defined in the spec.

src/main/java/org/springframework/hateoas/hal/DefaultCurieProvider.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* Default implementation of {@link CurieProvider} rendering a single configurable {@link UriTemplate} based curie.
2929
*
3030
* @author Oliver Gierke
31+
* @author Jeff Stano
3132
* @since 0.9
3233
*/
3334
public class DefaultCurieProvider implements CurieProvider {
@@ -65,8 +66,15 @@ public Collection<? extends Object> getCurieInformation(Links links) {
6566
*/
6667
@Override
6768
public String getNamespacedRelFrom(Link link) {
69+
return getNamespacedRelFor(link.getRel());
70+
}
6871

69-
String rel = link.getRel();
72+
/*
73+
* (non-Javadoc)
74+
* @see org.springframework.hateoas.hal.CurieProvider#getNamespacedRelFrom(java.lang.String)
75+
*/
76+
@Override
77+
public String getNamespacedRelFor(String rel) {
7078

7179
boolean prefixingNeeded = !IanaRels.isIanaRel(rel) && !rel.contains(":");
7280
return prefixingNeeded ? String.format("%s:%s", curie.name, rel) : rel;

src/main/java/org/springframework/hateoas/hal/HalEmbeddedBuilder.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class HalEmbeddedBuilder {
4242

4343
private final Map<String, Object> embeddeds = new HashMap<String, Object>();
4444
private final RelProvider provider;
45+
private final CurieProvider curieProvider;
4546
private final EmbeddedWrappers wrappers;
4647

4748
/**
@@ -50,19 +51,20 @@ class HalEmbeddedBuilder {
5051
* @param provider can be {@literal null}.
5152
* @param preferCollectionRels whether to prefer to ask the provider for collection rels.
5253
*/
53-
public HalEmbeddedBuilder(RelProvider provider, boolean preferCollectionRels) {
54+
public HalEmbeddedBuilder(RelProvider provider, CurieProvider curieProvider, boolean preferCollectionRels) {
5455

5556
Assert.notNull(provider, "Relprovider must not be null!");
5657

5758
this.provider = provider;
59+
this.curieProvider = curieProvider;
5860
this.wrappers = new EmbeddedWrappers(preferCollectionRels);
5961
}
6062

6163
/**
6264
* Adds the given value to the embeddeds. Will skip doing so if the value is {@literal null} or the content of a
6365
* {@link Resource} is {@literal null}.
6466
*
65-
* @param value can be {@literal null}.
67+
* @param source can be {@literal null}.
6668
*/
6769
public void add(Object source) {
6870

@@ -116,6 +118,11 @@ private String getDefaultedRelFor(EmbeddedWrapper wrapper, boolean forCollection
116118
Class<?> type = wrapper.getRelTargetType();
117119

118120
String rel = forCollection ? provider.getCollectionResourceRelFor(type) : provider.getItemResourceRelFor(type);
121+
122+
if (curieProvider != null) {
123+
rel = curieProvider.getNamespacedRelFor(rel);
124+
}
125+
119126
return rel == null ? DEFAULT_REL : rel;
120127
}
121128

src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,22 @@ public static class HalResourcesSerializer extends ContainerSerializer<Collectio
238238

239239
private final BeanProperty property;
240240
private final RelProvider relProvider;
241+
private final CurieProvider curieProvider;
241242
private final boolean enforceEmbeddedCollections;
242243

243-
public HalResourcesSerializer(RelProvider relPorvider, boolean enforceEmbeddedCollections) {
244-
this(null, relPorvider, enforceEmbeddedCollections);
244+
public HalResourcesSerializer(RelProvider relPorvider, CurieProvider curieProvider,
245+
boolean enforceEmbeddedCollections) {
246+
this(null, relPorvider, curieProvider, enforceEmbeddedCollections);
245247
}
246248

247-
public HalResourcesSerializer(BeanProperty property, RelProvider relProvider, boolean enforceEmbeddedCollections) {
249+
public HalResourcesSerializer(BeanProperty property, RelProvider relProvider, CurieProvider curieProvider,
250+
boolean enforceEmbeddedCollections) {
248251

249252
super(Collection.class, false);
250253

251254
this.property = property;
252255
this.relProvider = relProvider;
256+
this.curieProvider = curieProvider;
253257
this.enforceEmbeddedCollections = enforceEmbeddedCollections;
254258
}
255259

@@ -263,7 +267,7 @@ public HalResourcesSerializer(BeanProperty property, RelProvider relProvider, bo
263267
public void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
264268
JsonGenerationException {
265269

266-
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(relProvider, enforceEmbeddedCollections);
270+
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(relProvider, curieProvider, enforceEmbeddedCollections);
267271

268272
for (Object resource : value) {
269273
builder.add(resource);
@@ -275,7 +279,7 @@ public void serialize(Collection<?> value, JsonGenerator jgen, SerializerProvide
275279
@Override
276280
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
277281
throws JsonMappingException {
278-
return new HalResourcesSerializer(property, relProvider, enforceEmbeddedCollections);
282+
return new HalResourcesSerializer(property, relProvider, curieProvider, enforceEmbeddedCollections);
279283
}
280284

281285
@Override
@@ -600,7 +604,7 @@ public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider)
600604
public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider, boolean enforceEmbeddedCollections) {
601605

602606
Assert.notNull(resolver, "RelProvider must not be null!");
603-
this.instanceMap.put(HalResourcesSerializer.class, new HalResourcesSerializer(resolver,
607+
this.instanceMap.put(HalResourcesSerializer.class, new HalResourcesSerializer(resolver, curieProvider,
604608
enforceEmbeddedCollections));
605609
this.instanceMap.put(HalLinkListSerializer.class, new HalLinkListSerializer(curieProvider));
606610
}

src/test/java/org/springframework/hateoas/hal/DefaultCurieProviderUnitTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,28 @@ public void prefixesNormalRels() {
7272
public void doesNotPrefixQualifiedRels() {
7373
assertThat(provider.getNamespacedRelFrom(new Link("http://amazon.com", "custom:rel")), is("custom:rel"));
7474
}
75+
76+
/**
77+
* @see #229
78+
*/
79+
@Test
80+
public void doesNotPrefixIanaRelsForRelAsString() {
81+
assertThat(provider.getNamespacedRelFor("self"), is("self"));
82+
}
83+
84+
/**
85+
* @see #229
86+
*/
87+
@Test
88+
public void prefixesNormalRelsForRelAsString() {
89+
assertThat(provider.getNamespacedRelFor("book"), is("acme:book"));
90+
}
91+
92+
/**
93+
* @see #229
94+
*/
95+
@Test
96+
public void doesNotPrefixQualifiedRelsForRelAsString() {
97+
assertThat(provider.getNamespacedRelFor("custom:rel"), is("custom:rel"));
98+
}
7599
}

src/test/java/org/springframework/hateoas/hal/DefaultCurieProviderUnitTests.java

Lines changed: 0 additions & 75 deletions
This file was deleted.

src/test/java/org/springframework/hateoas/hal/HalEmbeddedBuilderUnitTest.java

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,31 @@
2525
import org.junit.Before;
2626
import org.junit.Test;
2727
import org.springframework.hateoas.RelProvider;
28+
import org.springframework.hateoas.UriTemplate;
2829
import org.springframework.hateoas.core.EmbeddedWrappers;
2930
import org.springframework.hateoas.core.EvoInflectorRelProvider;
3031

3132
/**
3233
* Unit tests for {@link HalEmbeddedBuilder}.
33-
*
34+
*
3435
* @author Oliver Gierke
3536
* @author Dietrich Schulten
3637
*/
3738
public class HalEmbeddedBuilderUnitTest {
3839

3940
RelProvider provider;
41+
CurieProvider curieProvider;
4042

4143
@Before
4244
public void setUp() {
4345
provider = new EvoInflectorRelProvider();
46+
curieProvider = new DefaultCurieProvider("curie", new UriTemplate("http://localhost/{rel}"));
4447
}
4548

4649
@Test
4750
public void rendersSingleElementsWithSingleEntityRel() {
4851

49-
Map<String, Object> map = setUpBuilder("foo", 1L);
52+
Map<String, Object> map = setUpBuilder(null, "foo", 1L);
5053

5154
assertThat(map.get("string"), is((Object) "foo"));
5255
assertThat(map.get("long"), is((Object) 1L));
@@ -55,7 +58,7 @@ public void rendersSingleElementsWithSingleEntityRel() {
5558
@Test
5659
public void rendersMultipleElementsWithCollectionResourceRel() {
5760

58-
Map<String, Object> map = setUpBuilder("foo", "bar", 1L);
61+
Map<String, Object> map = setUpBuilder(null, "foo", "bar", 1L);
5962

6063
assertThat(map.containsKey("string"), is(false));
6164
assertThat(map.get("long"), is((Object) 1L));
@@ -68,7 +71,7 @@ public void rendersMultipleElementsWithCollectionResourceRel() {
6871
@Test
6972
public void correctlyPilesUpResourcesInCollectionRel() {
7073

71-
Map<String, Object> map = setUpBuilder("foo", "bar", "foobar", 1L);
74+
Map<String, Object> map = setUpBuilder(null, "foo", "bar", "foobar", 1L);
7275

7376
assertThat(map.containsKey("string"), is(false));
7477
assertHasValues(map, "strings", "foo", "bar", "foobar");
@@ -81,7 +84,7 @@ public void correctlyPilesUpResourcesInCollectionRel() {
8184
@Test
8285
public void forcesCollectionRelToBeUsedIfConfigured() {
8386

84-
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(provider, true);
87+
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(provider, null, true);
8588
builder.add("Sample");
8689

8790
assertThat(builder.asMap().get("string"), is(nullValue()));
@@ -96,7 +99,7 @@ public void doesNotPreferCollectionsIfRelAwareWasAdded() {
9699

97100
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
98101

99-
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(provider, true);
102+
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(provider, null, true);
100103
builder.add(wrappers.wrap("MyValue", "foo"));
101104

102105
assertThat(builder.asMap().get("foo"), is(instanceOf(String.class)));
@@ -107,9 +110,61 @@ public void doesNotPreferCollectionsIfRelAwareWasAdded() {
107110
*/
108111
@Test(expected = IllegalArgumentException.class)
109112
public void rejectsNullRelProvider() {
110-
new HalEmbeddedBuilder(null, false);
113+
new HalEmbeddedBuilder(null, null, false);
114+
}
115+
116+
/**
117+
* @see #229
118+
*/
119+
@Test
120+
public void rendersSingleElementsWithSingleEntityRelWithCurieProvider() {
121+
122+
Map<String, Object> map = setUpBuilder(curieProvider, "foo", 1L);
123+
124+
assertThat(map.get("curie:string"), is((Object) "foo"));
125+
assertThat(map.get("curie:long"), is((Object) 1L));
126+
}
127+
128+
/**
129+
* @see #229
130+
*/
131+
@Test
132+
public void rendersMultipleElementsWithCollectionResourceRelWithCurieProvider() {
133+
134+
Map<String, Object> map = setUpBuilder(curieProvider, "foo", "bar", 1L);
135+
136+
assertThat(map.containsKey("curie:string"), is(false));
137+
assertThat(map.get("curie:long"), is((Object) 1L));
138+
assertHasValues(map, "curie:strings", "foo", "bar");
139+
}
140+
141+
/**
142+
* @see #229
143+
*/
144+
@Test
145+
public void correctlyPilesUpResourcesInCollectionRelWithCurieprovider() {
146+
147+
Map<String, Object> map = setUpBuilder(curieProvider, "foo", "bar", "foobar", 1L);
148+
149+
assertThat(map.containsKey("curie:string"), is(false));
150+
assertHasValues(map, "curie:strings", "foo", "bar", "foobar");
151+
assertThat(map.get("curie:long"), is((Object) 1L));
152+
}
153+
154+
/**
155+
* @see #229
156+
*/
157+
@Test
158+
public void forcesCollectionRelToBeUsedIfConfiguredWithCurieProvider() {
159+
160+
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(provider, curieProvider, true);
161+
builder.add("Sample");
162+
163+
assertThat(builder.asMap().get("curie:string"), is(nullValue()));
164+
assertHasValues(builder.asMap(), "curie:strings", "Sample");
111165
}
112166

167+
@SuppressWarnings("unchecked")
113168
private static void assertHasValues(Map<String, Object> source, String rel, Object... values) {
114169

115170
Object value = source.get(rel);
@@ -118,9 +173,9 @@ private static void assertHasValues(Map<String, Object> source, String rel, Obje
118173
assertThat((List<Object>) value, Matchers.<List<Object>> allOf(hasSize(values.length), hasItems(values)));
119174
}
120175

121-
private Map<String, Object> setUpBuilder(Object... values) {
176+
private Map<String, Object> setUpBuilder(CurieProvider curieProvider, Object... values) {
122177

123-
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(provider, false);
178+
HalEmbeddedBuilder builder = new HalEmbeddedBuilder(provider, curieProvider, false);
124179

125180
for (Object value : values) {
126181
builder.add(value);

0 commit comments

Comments
 (0)