diff --git a/README.md b/README.md
index f8691be62..a13d16471 100644
--- a/README.md
+++ b/README.md
@@ -11,9 +11,69 @@ managing associations.
### Installation
To use the Spring Data Rest exporter, first package your domain classes and repositories into a JAR
-file. Include some Spring XML configuration files in the `META-INF/spring-data-rest` directory in
-that JAR file (including an applicable EntityManager and DataSource).
+file. Include some Spring XML configuration files in the `META-INF/spring-data-rest` directory (the
+file name should end with "-export.xml" to be picked up by the scanner) in that JAR file that include
+an applicable EntityManager and DataSource and the Repository configuration (using the special JPA
+Repository namespace).
You can either deploy this JAR file into your Servlet container in a "shared" configuration, or you
-can add this JAR file (and any other application dependencies to the exporter WAR file's `WEB-INF/lib`
+can add this JAR file (and any other application dependencies) to the exporter WAR file's `WEB-INF/lib`
directory.
+
+Somewhere in the Spring configuration files you need to define a bean called "baseUri" that is a
+`java.net.URI` and is the fully-qualified URI in which the exporter servlet has been deployed. In the
+case of the sample below, the servlet is deployed to a context path of `/data`. Using the default
+host and port settings, this yields a `baseUri` of `http://localhost:8080/data`. You'll want to change
+this to reflect your deployment configuration.
+
+### Sample Configuration
+
+The configuration used in testing looks like this:
+
+##### META-INF/spring-data-rest/shared.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+##### META-INF/spring-data-rest/repositories-export.xml
+
+
+
+
+
+
+
+
+
+
diff --git a/rest/src/main/java/org/springframework/data/rest/mvc/RepositoryRestController.java b/rest/src/main/java/org/springframework/data/rest/mvc/RepositoryRestController.java
index 75b224fd5..6fa634700 100644
--- a/rest/src/main/java/org/springframework/data/rest/mvc/RepositoryRestController.java
+++ b/rest/src/main/java/org/springframework/data/rest/mvc/RepositoryRestController.java
@@ -204,10 +204,6 @@ public class RepositoryRestController implements InitializingBean {
URI relativeUri = baseUri.relativize(request.getURI());
final Stack uris = UriUtils.explode(baseUri, relativeUri);
- if (LOG.isDebugEnabled()) {
- LOG.debug("uris: " + uris);
- }
-
final int uriCnt = uris.size();
if (uris.size() > 0) {
final String repoName = uris.get(0).getPath();
@@ -448,6 +444,11 @@ public class RepositoryRestController implements InitializingBean {
repo.findOne(serId) :
entityMetadata.targetType().newInstance();
+ if (null == entity) {
+ model.addAttribute(STATUS, HttpStatus.NOT_FOUND);
+ return;
+ }
+
entityMetadata.doWithEmbedded(new Handler() {
@Override public Void handle(Attribute attribute) {
String name = attribute.getName();
diff --git a/rest/src/test/groovy/org/springframework/data/rest/mvc/spec/RepositoryRestControllerSpec.groovy b/rest/src/test/groovy/org/springframework/data/rest/mvc/spec/RepositoryRestControllerSpec.groovy
index 1d7c0aa5a..830245b9e 100644
--- a/rest/src/test/groovy/org/springframework/data/rest/mvc/spec/RepositoryRestControllerSpec.groovy
+++ b/rest/src/test/groovy/org/springframework/data/rest/mvc/spec/RepositoryRestControllerSpec.groovy
@@ -14,6 +14,7 @@ import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.test.context.ContextConfiguration
import org.springframework.transaction.annotation.Transactional
import org.springframework.ui.ExtendedModelMap
+import org.springframework.ui.Model
import spock.lang.Shared
import spock.lang.Specification
@@ -30,21 +31,47 @@ class RepositoryRestControllerSpec extends Specification {
@Autowired
RepositoryRestController controller
- ServletServerHttpRequest createRequest(String method, String path) {
- return new ServletServerHttpRequest(new MockHttpServletRequest(
+ MockHttpServletRequest createRequest(String method, String path) {
+ return new MockHttpServletRequest(
serverPort: 8080,
requestURI: "/data/$path",
method: method
- ))
+ )
}
- Map GET(String path) {
+ Model GET(String path) {
def request = createRequest("GET", path)
def model = new ExtendedModelMap()
- controller.get(request, model)
+ controller.get(new ServletServerHttpRequest(request), model)
return model
}
+ Model POST(String path, m) {
+ def request = createRequest("POST", path)
+ request.contentType = "application/json"
+ request.setContent(mapper.writeValueAsBytes(m))
+ def model = new ExtendedModelMap()
+ controller.createOrUpdate(new ServletServerHttpRequest(request), model)
+ return model
+ }
+
+ Model PUT(String path, m) {
+ def request = createRequest("PUT", path)
+ request.contentType = "application/json"
+ request.setContent(mapper.writeValueAsBytes(m))
+ def model = new ExtendedModelMap()
+ controller.createOrUpdate(new ServletServerHttpRequest(request), model)
+ return model
+ }
+
+ Model DELETE(String path) {
+ def request = createRequest("DELETE", path)
+ def model = new ExtendedModelMap()
+ controller.delete(new ServletServerHttpRequest(request), model)
+ return model
+
+ }
+
def setupSpec() {
def customSerializerFactory = new CustomSerializerFactory()
customSerializerFactory.addSpecificMapping(SimpleLink, new FluentBeanSerializer(SimpleLink))
@@ -74,15 +101,86 @@ class RepositoryRestControllerSpec extends Specification {
def person = GET("person/1")
then:
- person?.resource.name == "John Doe"
+ person?.resource?.name == "John Doe"
when:
def profiles = GET("person/1/profiles")
- def profilesLinks = profiles?.resource.profiles
+ def profilesLinks = profiles.resource?.profiles
then:
profilesLinks.size() == 2
}
+ @Transactional
+ def "responds to POST with ID"() {
+
+ when:
+ def created = POST("person/3", [name: "James Doe"])
+
+ then:
+ created.status == HttpStatus.CREATED
+
+ }
+
+ @Transactional
+ def "responds to PUT"() {
+
+ given:
+ POST("person/3", [name: "James Doe"])
+
+ when:
+ def updated = PUT("person/3", [name: "James Doe Jr."])
+ def getUpdated = GET("person/3")
+
+ then:
+ updated.status == HttpStatus.NO_CONTENT
+ getUpdated.status == HttpStatus.OK
+ getUpdated.resource?.name == "James Doe Jr."
+
+ }
+
+ @Transactional
+ def "updates links"() {
+
+ given:
+ POST("person/3", [name: "James Doe"])
+
+ when:
+ def link = POST("person/3/addresses", [[href: "$baseUri/address/1".toString()]])
+ def getUpdated = GET("person/3/addresses")
+
+ then:
+ link.status == HttpStatus.CREATED
+ getUpdated.status == HttpStatus.OK
+ getUpdated.resource?.size() == 1
+
+ }
+
+ @Transactional
+ def "responds to DELETE"() {
+
+ given:
+ POST("person/3", [name: "James Doe"])
+ POST("person/3/addresses", [[href: "$baseUri/address/1".toString()]])
+
+ when:
+ def deleted = DELETE("person/3/addresses/1")
+ def getUpdated = GET("person/3/addresses")
+
+ then:
+ deleted.status == HttpStatus.NO_CONTENT
+ getUpdated.status == HttpStatus.OK
+ getUpdated.resource?._links?.size() == 0
+
+ when:
+ def delEntity = DELETE("person/3")
+ def getUpdEntity = GET("person/3")
+
+ then:
+ delEntity.status == HttpStatus.NO_CONTENT
+ getUpdEntity.status == HttpStatus.NOT_FOUND
+
+ }
+
}