Skip to content

Commit 0de282b

Browse files
committed
spring-projects#144 - Added sample for URI customization in Spring Data REST.
1 parent c6b3ee8 commit 0de282b

File tree

9 files changed

+298
-0
lines changed

9 files changed

+298
-0
lines changed

rest/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<module>projections</module>
2121
<module>security</module>
2222
<module>headers</module>
23+
<module>uri-customization</module>
2324
</modules>
2425

2526
<dependencies>

rest/uri-customization/pom.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<artifactId>spring-data-rest-uri-customizations</artifactId>
6+
7+
<name>Spring Data REST - URI Customizations Example</name>
8+
9+
<parent>
10+
<groupId>org.springframework.data.examples</groupId>
11+
<artifactId>spring-data-rest-examples</artifactId>
12+
<version>1.0.0.BUILD-SNAPSHOT</version>
13+
</parent>
14+
15+
<properties>
16+
<spring-data-releasetrain.version>Hopper-BUILD-SNAPSHOT</spring-data-releasetrain.version>
17+
</properties>
18+
19+
<dependencies>
20+
21+
<dependency>
22+
<groupId>org.springframework.data</groupId>
23+
<artifactId>spring-data-keyvalue</artifactId>
24+
</dependency>
25+
26+
<dependency>
27+
<groupId>com.jayway.jsonpath</groupId>
28+
<artifactId>json-path</artifactId>
29+
<scope>test</scope>
30+
</dependency>
31+
32+
</dependencies>
33+
34+
</project>

rest/uri-customization/readme.adoc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
= Spring Data REST URI customizations
2+
3+
This example shows how to customize which property of the domain type shall be used to create URIs for item resources. This is achieved by implementing an `EntityLookup` and declaring it as Spring bean:
4+
5+
[source, java]
6+
----
7+
@Component
8+
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
9+
public class UserEntityLookup extends EntityLookupSupport<User> {
10+
11+
private final @NonNull UserRepository repository;
12+
13+
@Override
14+
public Serializable getResourceIdentifier(User entity) {
15+
return entity.getUsername();
16+
}
17+
18+
@Override
19+
public Object lookupEntity(Serializable id) {
20+
return repository.findByUsername(id.toString());
21+
}
22+
}
23+
----
24+
25+
As you can see the customization consists of two methods that need to be symmetric in their functionality. `getResourceIdentifier(…)` returns the property that's supposed to be used in the URI while `lookupEntity(…)` uses the value to lookup an entity via a query method for exactly that property.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2015 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 example.springdata.rest.uris;
17+
18+
import javax.annotation.PostConstruct;
19+
20+
import org.springframework.beans.factory.annotation.Autowired;
21+
import org.springframework.boot.SpringApplication;
22+
import org.springframework.boot.autoconfigure.SpringBootApplication;
23+
import org.springframework.data.map.repository.config.EnableMapRepositories;
24+
25+
/**
26+
* Applicatoin class to bootstrap the app.
27+
*
28+
* @author Oliver Gierke
29+
*/
30+
@SpringBootApplication
31+
@EnableMapRepositories
32+
public class Application {
33+
34+
public static void main(String[] args) {
35+
SpringApplication.run(Application.class, args);
36+
}
37+
38+
@Autowired UserRepository repository;
39+
40+
@PostConstruct
41+
public void init() {
42+
43+
repository.save(new User("olivergierke"));
44+
repository.save(new User("starbucksman"));
45+
}
46+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2015 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 example.springdata.rest.uris;
17+
18+
import lombok.Value;
19+
20+
import java.util.UUID;
21+
22+
import org.springframework.data.annotation.Id;
23+
24+
/**
25+
* A {@link User}.
26+
*
27+
* @author Oliver Gierke
28+
*/
29+
@Value
30+
public class User {
31+
32+
private final @Id UUID id = UUID.randomUUID();
33+
private final String username;
34+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2015 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 example.springdata.rest.uris;
17+
18+
import lombok.NonNull;
19+
import lombok.RequiredArgsConstructor;
20+
21+
import java.io.Serializable;
22+
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.data.rest.core.support.EntityLookup;
25+
import org.springframework.data.rest.core.support.EntityLookupSupport;
26+
import org.springframework.stereotype.Component;
27+
28+
/**
29+
* Custom {@link EntityLookup} to replace the usage of the database identifier in item resource URIs with the username
30+
* property of the {@link User}.
31+
*
32+
* @author Oliver Gierke
33+
*/
34+
@Component
35+
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
36+
public class UserEntityLookup extends EntityLookupSupport<User> {
37+
38+
private final @NonNull UserRepository repository;
39+
40+
/*
41+
* (non-Javadoc)
42+
* @see org.springframework.data.rest.core.support.EntityLookup#getId(java.lang.Object)
43+
*/
44+
@Override
45+
public Serializable getResourceIdentifier(User entity) {
46+
return entity.getUsername();
47+
}
48+
49+
/*
50+
* (non-Javadoc)
51+
* @see org.springframework.data.rest.core.support.EntityLookup#lookupEntity(java.io.Serializable)
52+
*/
53+
@Override
54+
public Object lookupEntity(Serializable id) {
55+
return repository.findByUsername(id.toString());
56+
}
57+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2015 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 example.springdata.rest.uris;
17+
18+
import java.util.Optional;
19+
import java.util.UUID;
20+
21+
import org.springframework.data.repository.CrudRepository;
22+
23+
/**
24+
* Repository to manage {@link User} instances.
25+
*
26+
* @author Oliver Gierke
27+
*/
28+
public interface UserRepository extends CrudRepository<User, UUID> {
29+
30+
/**
31+
* Looks up a unique {@link User} by its user name.
32+
*
33+
* @param username
34+
* @return
35+
*/
36+
Optional<User> findByUsername(String username);
37+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2015 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 example.springdata.rest.uris;
17+
18+
import static org.hamcrest.CoreMatchers.*;
19+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
20+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
21+
22+
import org.junit.Before;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.boot.test.SpringApplicationConfiguration;
27+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
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.web.context.WebApplicationContext;
32+
33+
/**
34+
* Integration tests to make sure the URI customizations are applied.
35+
*
36+
* @author Oliver Gierke
37+
* @soundtrack Clueso - Gewinner (Stadtrandlichter Live)
38+
*/
39+
@RunWith(SpringJUnit4ClassRunner.class)
40+
@WebAppConfiguration
41+
@SpringApplicationConfiguration(classes = Application.class)
42+
public class WebIntegrationTests {
43+
44+
@Autowired WebApplicationContext context;
45+
@Autowired UserRepository users;
46+
47+
MockMvc mvc;
48+
49+
@Before
50+
public void setUp() {
51+
52+
this.mvc = MockMvcBuilders.webAppContextSetup(context).//
53+
build();
54+
}
55+
56+
@Test
57+
public void identifiesResourcesUsingUsername() throws Exception {
58+
59+
mvc.perform(get("/users/olivergierke")).//
60+
andExpect(status().isOk()).//
61+
andExpect(jsonPath("$._links.self.href", endsWith("olivergierke")));
62+
}
63+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.springframework.restdocs.outputDir=target/generated-snippets

0 commit comments

Comments
 (0)