Skip to content

Commit d23a6fd

Browse files
author
Paul Warren
committed
Refactor content link generation
- so that content links in an app with a context path are consrtucted correctly
1 parent 036d416 commit d23a6fd

File tree

5 files changed

+165
-21
lines changed

5 files changed

+165
-21
lines changed

spring-content-rest/src/main/java/internal/org/springframework/content/rest/links/ContentLinksResourceProcessor.java

+14-18
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.util.Assert;
4444
import org.springframework.util.ReflectionUtils;
4545
import org.springframework.util.StringUtils;
46+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
4647
import org.springframework.web.util.UriComponentsBuilder;
4748

4849
import static java.lang.String.format;
@@ -228,15 +229,15 @@ private Optional<Link> originalLink(URI baseUri, ContentStoreInfo store, Object
228229
private Link shortcutLink(URI baseUri, ContentStoreInfo store, Object id, String rel) {
229230

230231
LinkBuilder builder = null;
231-
builder = StoreLinkBuilder.linkTo(store, new BaseUri(baseUri));
232+
builder = StoreLinkBuilder.linkTo(new BaseUri(baseUri), store);
232233

233234
builder = builder.slash(id);
234235

235236
return builder.withRel(rel);
236237
}
237238

238239
private Link fullyQualifiedLink(URI baseUri, ContentStoreInfo store, Object id, String fieldName) {
239-
LinkBuilder builder = StoreLinkBuilder.linkTo(store, new BaseUri(baseUri));
240+
LinkBuilder builder = StoreLinkBuilder.linkTo(new BaseUri(baseUri), store);
240241

241242
builder = builder.slash(id);
242243

@@ -247,7 +248,7 @@ private Link fullyQualifiedLink(URI baseUri, ContentStoreInfo store, Object id,
247248
}
248249

249250
private Link propertyLink(URI baseUri, ContentStoreInfo store, Object id, String property, String contentId) {
250-
LinkBuilder builder = StoreLinkBuilder.linkTo(store, new BaseUri(baseUri));
251+
LinkBuilder builder = StoreLinkBuilder.linkTo(new BaseUri(baseUri), store);
251252

252253
builder = builder.slash(id).slash(property);
253254

@@ -260,8 +261,8 @@ private Link propertyLink(URI baseUri, ContentStoreInfo store, Object id, String
260261

261262
public static class StoreLinkBuilder extends LinkBuilderSupport<StoreLinkBuilder> {
262263

263-
public StoreLinkBuilder(BaseUri baseUri) {
264-
super(baseUri.getUriComponentsBuilder());
264+
public StoreLinkBuilder(BaseUri baseUri, ContentStoreInfo store) {
265+
super(baseUri.getUriComponentsBuilder().path(storePath(store)));
265266
}
266267

267268
@Override
@@ -271,24 +272,19 @@ protected StoreLinkBuilder getThis() {
271272

272273
@Override
273274
protected StoreLinkBuilder createNewInstance(UriComponentsBuilder builder, List list) {
274-
return new StoreLinkBuilder(new BaseUri(builder.toUriString()));
275+
return new StoreLinkBuilder(new BaseUri(builder.toUriString()), null);
275276
}
276277

277-
public static StoreLinkBuilder linkTo(ContentStoreInfo store, BaseUri baseUri) {
278-
return new StoreLinkBuilder(new BaseUri(storePath(store, baseUri)));
278+
public static StoreLinkBuilder linkTo(BaseUri baseUri, ContentStoreInfo store) {
279+
return new StoreLinkBuilder(baseUri, store);
279280
}
280281

281-
private static String storePath(ContentStoreInfo store, BaseUri baseUri) {
282-
String storePath = ContentStoreUtils.storePath(store);
283-
if (!StringUtils.isEmpty(baseUri.getUri().toString())) {
284-
String basePath = baseUri.getUri().toString();
285-
if (basePath.startsWith("/")) {
286-
basePath = StringUtils.trimLeadingCharacter(basePath, '/');
287-
}
288-
289-
storePath = format("%s/%s", basePath, storePath);
282+
private static String storePath(ContentStoreInfo store) {
283+
if (store == null) {
284+
return "";
290285
}
291-
return storePath;
286+
287+
return format("/%s", ContentStoreUtils.storePath(store));
292288
}
293289
}
294290
}

spring-content-rest/src/test/java/internal/org/springframework/content/rest/controllers/ContextPathTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public class ContextPathTest {
6868
private Content contentTests;
6969

7070
{
71-
Describe("BaseUri Content Tests", () -> {
71+
Describe("ContextPath Content Tests", () -> {
7272
BeforeEach(() -> {
7373
mvc = MockMvcBuilders.webAppContextSetup(context).build();
7474
});

spring-content-rest/src/test/java/internal/org/springframework/content/rest/links/BaseUriContentLinksIntegrationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public class BaseUriContentLinksIntegrationTest {
9090
entityContentLinkTests.setStore(contentRepository3);
9191
entityContentLinkTests.setTestEntity(testEntity3);
9292
entityContentLinkTests.setUrl("/api/testEntity3s/" + testEntity3.getId());
93-
entityContentLinkTests.setLinkRel("testEntity3");
93+
entityContentLinkTests.setLinkRel("testEntity3s");
9494
entityContentLinkTests.setExpectedLinkRegex("http://localhost/contentApi/testEntity3s/" + testEntity3.getId());
9595
});
9696
entityContentLinkTests = new EntityContentLinkTests();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package internal.org.springframework.content.rest.links;
2+
3+
import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.BeforeEach;
4+
import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.Context;
5+
import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.Describe;
6+
7+
import java.io.ByteArrayInputStream;
8+
import java.io.File;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
12+
import org.apache.commons.io.IOUtils;
13+
import org.junit.Test;
14+
import org.junit.runner.RunWith;
15+
import org.springframework.beans.factory.annotation.Autowired;
16+
import org.springframework.content.commons.renditions.RenditionProvider;
17+
import org.springframework.content.fs.config.EnableFilesystemStores;
18+
import org.springframework.content.fs.io.FileSystemResourceLoader;
19+
import org.springframework.content.rest.config.HypermediaConfiguration;
20+
import org.springframework.content.rest.config.RestConfiguration;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.context.annotation.Profile;
24+
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
25+
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
26+
import org.springframework.test.context.ActiveProfiles;
27+
import org.springframework.test.context.ContextConfiguration;
28+
import org.springframework.test.context.web.WebAppConfiguration;
29+
import org.springframework.test.web.servlet.MockMvc;
30+
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
31+
import org.springframework.transaction.annotation.EnableTransactionManagement;
32+
import org.springframework.transaction.annotation.Transactional;
33+
import org.springframework.web.context.WebApplicationContext;
34+
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
35+
36+
import com.github.paulcwarren.ginkgo4j.Ginkgo4jConfiguration;
37+
import com.github.paulcwarren.ginkgo4j.Ginkgo4jSpringRunner;
38+
39+
import internal.org.springframework.content.rest.support.TestEntity3;
40+
import internal.org.springframework.content.rest.support.TestEntity3ContentRepository;
41+
import internal.org.springframework.content.rest.support.TestEntity3Repository;
42+
import internal.org.springframework.content.rest.support.config.JpaInfrastructureConfig;
43+
44+
@RunWith(Ginkgo4jSpringRunner.class)
45+
@Ginkgo4jConfiguration(threads = 1)
46+
@WebAppConfiguration
47+
@ContextConfiguration(classes = {
48+
ContextPathContentLinksIntegrationTest.ContextPathConfig.class,
49+
DelegatingWebMvcConfiguration.class,
50+
RepositoryRestMvcConfiguration.class,
51+
RestConfiguration.class,
52+
HypermediaConfiguration.class })
53+
@Transactional
54+
@ActiveProfiles("store")
55+
public class ContextPathContentLinksIntegrationTest {
56+
57+
@Autowired
58+
TestEntity3Repository repository3;
59+
@Autowired
60+
TestEntity3ContentRepository contentRepository3;
61+
62+
@Autowired
63+
private WebApplicationContext context;
64+
65+
private MockMvc mvc;
66+
67+
private TestEntity3 testEntity3;
68+
69+
private EntityContentLinkTests entityContentLinkTests;
70+
71+
{
72+
Describe("given the spring content baseUri property is set to contentApi", () -> {
73+
BeforeEach(() -> {
74+
mvc = MockMvcBuilders.webAppContextSetup(context).build();
75+
});
76+
77+
Context("given an Entity and a Store with a default store path", () -> {
78+
BeforeEach(() -> {
79+
testEntity3 = repository3.save(new TestEntity3());
80+
81+
entityContentLinkTests.setMvc(mvc);
82+
entityContentLinkTests.setRepository(repository3);
83+
entityContentLinkTests.setStore(contentRepository3);
84+
entityContentLinkTests.setTestEntity(testEntity3);
85+
entityContentLinkTests.setUrl("/contextPath/testEntity3s/" + testEntity3.getId());
86+
entityContentLinkTests.setContextPath("/contextPath");
87+
entityContentLinkTests.setLinkRel("testEntity3s");
88+
entityContentLinkTests.setExpectedLinkRegex("http://localhost/contextPath/testEntity3s/" + testEntity3.getId());
89+
});
90+
entityContentLinkTests = new EntityContentLinkTests();
91+
});
92+
});
93+
}
94+
95+
@Configuration
96+
@EnableJpaRepositories(basePackages = "internal.org.springframework.content.rest.support")
97+
@EnableTransactionManagement
98+
@EnableFilesystemStores(basePackages = "internal.org.springframework.content.rest.support")
99+
@Profile("store")
100+
public static class ContextPathConfig extends JpaInfrastructureConfig {
101+
102+
@Bean
103+
FileSystemResourceLoader fileSystemResourceLoader() {
104+
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
105+
}
106+
107+
@Bean
108+
public File filesystemRoot() {
109+
File baseDir = new File(System.getProperty("java.io.tmpdir"));
110+
File filesystemRoot = new File(baseDir, "spring-content-controller-tests");
111+
filesystemRoot.mkdirs();
112+
return filesystemRoot;
113+
}
114+
115+
@Bean
116+
public RenditionProvider textToHtml() {
117+
return new RenditionProvider() {
118+
119+
@Override
120+
public String consumes() {
121+
return "text/plain";
122+
}
123+
124+
@Override
125+
public String[] produces() {
126+
return new String[] { "text/html" };
127+
}
128+
129+
@Override
130+
public InputStream convert(InputStream fromInputSource, String toMimeType) {
131+
String input = null;
132+
try {
133+
input = IOUtils.toString(fromInputSource);
134+
}
135+
catch (IOException e) {
136+
}
137+
return new ByteArrayInputStream(
138+
String.format("<html><body>%s</body></html>", input).getBytes());
139+
}
140+
};
141+
}
142+
}
143+
144+
@Test
145+
public void noop() {}
146+
}

spring-content-rest/src/test/java/internal/org/springframework/content/rest/links/EntityContentLinkTests.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class EntityContentLinkTests {
3838

3939
private Object testEntity;
4040
private String url;
41+
private String contextPath = "";
4142
private String linkRel;
4243
private String expectedLinkRegex;
4344

@@ -50,7 +51,8 @@ public class EntityContentLinkTests {
5051
Context("a GET to /{api}?/{repository}/{id}", () -> {
5152
It("should provide a response with a content link", () -> {
5253
MockHttpServletResponse response = mvc.perform(get(url)
53-
.accept("application/hal+json"))
54+
.accept("application/hal+json")
55+
.contextPath(contextPath))
5456
.andExpect(status().isOk()).andReturn().getResponse();
5557
assertThat(response, is(not(nullValue())));
5658

0 commit comments

Comments
 (0)