#144 - Added sample for URI customization in Spring Data REST.

This commit is contained in:
Oliver Gierke
2015-12-09 13:01:21 +01:00
parent c6b3ee8b5b
commit 0de282b2f2
9 changed files with 298 additions and 0 deletions

View File

@@ -20,6 +20,7 @@
<module>projections</module>
<module>security</module>
<module>headers</module>
<module>uri-customization</module>
</modules>
<dependencies>

View File

@@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-rest-uri-customizations</artifactId>
<name>Spring Data REST - URI Customizations Example</name>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-rest-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<properties>
<spring-data-releasetrain.version>Hopper-BUILD-SNAPSHOT</spring-data-releasetrain.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-keyvalue</artifactId>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
= Spring Data REST URI customizations
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:
[source, java]
----
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
public class UserEntityLookup extends EntityLookupSupport<User> {
private final @NonNull UserRepository repository;
@Override
public Serializable getResourceIdentifier(User entity) {
return entity.getUsername();
}
@Override
public Object lookupEntity(Serializable id) {
return repository.findByUsername(id.toString());
}
}
----
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.

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.rest.uris;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.map.repository.config.EnableMapRepositories;
/**
* Applicatoin class to bootstrap the app.
*
* @author Oliver Gierke
*/
@SpringBootApplication
@EnableMapRepositories
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired UserRepository repository;
@PostConstruct
public void init() {
repository.save(new User("olivergierke"));
repository.save(new User("starbucksman"));
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.rest.uris;
import lombok.Value;
import java.util.UUID;
import org.springframework.data.annotation.Id;
/**
* A {@link User}.
*
* @author Oliver Gierke
*/
@Value
public class User {
private final @Id UUID id = UUID.randomUUID();
private final String username;
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.rest.uris;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.io.Serializable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.core.support.EntityLookup;
import org.springframework.data.rest.core.support.EntityLookupSupport;
import org.springframework.stereotype.Component;
/**
* Custom {@link EntityLookup} to replace the usage of the database identifier in item resource URIs with the username
* property of the {@link User}.
*
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
public class UserEntityLookup extends EntityLookupSupport<User> {
private final @NonNull UserRepository repository;
/*
* (non-Javadoc)
* @see org.springframework.data.rest.core.support.EntityLookup#getId(java.lang.Object)
*/
@Override
public Serializable getResourceIdentifier(User entity) {
return entity.getUsername();
}
/*
* (non-Javadoc)
* @see org.springframework.data.rest.core.support.EntityLookup#lookupEntity(java.io.Serializable)
*/
@Override
public Object lookupEntity(Serializable id) {
return repository.findByUsername(id.toString());
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.rest.uris;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.repository.CrudRepository;
/**
* Repository to manage {@link User} instances.
*
* @author Oliver Gierke
*/
public interface UserRepository extends CrudRepository<User, UUID> {
/**
* Looks up a unique {@link User} by its user name.
*
* @param username
* @return
*/
Optional<User> findByUsername(String username);
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.rest.uris;
import static org.hamcrest.CoreMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* Integration tests to make sure the URI customizations are applied.
*
* @author Oliver Gierke
* @soundtrack Clueso - Gewinner (Stadtrandlichter Live)
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = Application.class)
public class WebIntegrationTests {
@Autowired WebApplicationContext context;
@Autowired UserRepository users;
MockMvc mvc;
@Before
public void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(context).//
build();
}
@Test
public void identifiesResourcesUsingUsername() throws Exception {
mvc.perform(get("/users/olivergierke")).//
andExpect(status().isOk()).//
andExpect(jsonPath("$._links.self.href", endsWith("olivergierke")));
}
}

View File

@@ -0,0 +1 @@
org.springframework.restdocs.outputDir=target/generated-snippets