DATAREST-203 - Added test cases to show POSTs are generally working.

Some polishing in PersistentEntityResourceArgumentResolver.
This commit is contained in:
Oliver Gierke
2013-12-28 18:04:46 +01:00
parent c953e6f54d
commit 8c13ee3b7e
6 changed files with 115 additions and 12 deletions

View File

@@ -27,10 +27,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
* A Spring HATEOAS {@link Resource} subclass that holds a reference to the entity's {@link PersistentEntity} metadata.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
public class PersistentEntityResource<T> extends Resource<T> {
@JsonIgnore private final PersistentEntity<?, ?> entity;
private final PersistentEntity<?, ?> entity;
public static <T> PersistentEntityResource<T> wrap(PersistentEntity<?, ?> entity, T obj) {
return new PersistentEntityResource<T>(entity, obj);
@@ -45,6 +46,7 @@ public class PersistentEntityResource<T> extends Resource<T> {
this.entity = entity;
}
@JsonIgnore
public PersistentEntity<?, ?> getPersistentEntity() {
return entity;
}

View File

@@ -1,35 +1,79 @@
/*
* Copyright 2012-2013 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 org.springframework.data.rest.webmvc;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Custom {@link HandlerMethodArgumentResolver} to create {@link PersistentEntityResource} instances.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
public class PersistentEntityResourceHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired private RepositoryRestRequestHandlerMethodArgumentResolver repoRequestResolver;
private static final String ERROR_MESSAGE = "Could not read an object of type %s from the request! Converter %s returned null!";
private static final String NO_CONVERTER_FOUND = "No suitable HttpMessageConverter found to read request body into object of type %s from request with content type of %s!";
private final RepositoryRestRequestHandlerMethodArgumentResolver repoRequestResolver;
private final List<HttpMessageConverter<?>> messageConverters;
public PersistentEntityResourceHandlerMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
/**
* Creates a new {@link PersistentEntityResourceHandlerMethodArgumentResolver} for the given
* {@link HttpMessageConverter}s and {@link RepositoryRestRequestHandlerMethodArgumentResolver}..
*
* @param messageConverters must not be {@literal null}.
* @param repositoryRequestResolver must not be {@literal null}.
*/
public PersistentEntityResourceHandlerMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters,
RepositoryRestRequestHandlerMethodArgumentResolver repositoryRequestResolver) {
Assert.notEmpty(messageConverters, "MessageConverters must not be null or empty!");
Assert.notNull(repositoryRequestResolver, "RepositoryRestRequestHandlerMethodArgumentResolver must not be empty!");
this.messageConverters = messageConverters;
this.repoRequestResolver = repositoryRequestResolver;
}
/*
* (non-Javadoc)
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return PersistentEntityResource.class.isAssignableFrom(parameter.getParameterType());
}
/*
* (non-Javadoc)
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
@@ -37,19 +81,27 @@ public class PersistentEntityResourceHandlerMethodArgumentResolver implements Ha
RepositoryRestRequest repoRequest = (RepositoryRestRequest) repoRequestResolver.resolveArgument(parameter,
mavContainer, webRequest, binderFactory);
final ServletServerHttpRequest request = new ServletServerHttpRequest(
webRequest.getNativeRequest(HttpServletRequest.class));
HttpServletRequest nativeRequest = webRequest.getNativeRequest(HttpServletRequest.class);
ServletServerHttpRequest request = new ServletServerHttpRequest(nativeRequest);
Class<?> domainType = repoRequest.getPersistentEntity().getType();
MediaType contentType = request.getHeaders().getContentType();
for (HttpMessageConverter converter : messageConverters) {
Class<?> domainType = repoRequest.getPersistentEntity().getType();
if (!converter.canRead(domainType, request.getHeaders().getContentType())) {
if (!converter.canRead(domainType, contentType)) {
continue;
}
Object obj = converter.read(domainType, request);
if (obj == null) {
throw new HttpMessageNotReadableException(String.format(ERROR_MESSAGE, domainType, converter));
}
return new PersistentEntityResource<Object>(repoRequest.getPersistentEntity(), obj);
}
return null;
throw new HttpMessageNotReadableException(String.format(NO_CONVERTER_FOUND, domainType, contentType));
}
}

View File

@@ -254,10 +254,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
*/
@Bean
public PersistentEntityResourceHandlerMethodArgumentResolver persistentEntityArgumentResolver() {
List<HttpMessageConverter<?>> messageConverters = defaultMessageConverters();
configureHttpMessageConverters(messageConverters);
return new PersistentEntityResourceHandlerMethodArgumentResolver(messageConverters);
return new PersistentEntityResourceHandlerMethodArgumentResolver(messageConverters, repoRequestArgumentResolver());
}
/**
@@ -432,7 +433,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
return new DefaultRepositoryInvokerFactory(repositories(), defaultConversionService());
}
private List<HttpMessageConverter<?>> defaultMessageConverters() {
@Bean
public List<HttpMessageConverter<?>> defaultMessageConverters() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();

View File

@@ -17,10 +17,13 @@ package org.springframework.data.rest.webmvc;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
@@ -40,6 +43,7 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@@ -272,5 +276,34 @@ public abstract class AbstractWebIntegrationTests {
}
}
@Test
public void postsPayloadToResource() throws Exception {
Map<String, String> payloads = getPayloadToPost();
assumeFalse(payloads.isEmpty());
MockHttpServletResponse response = request("/");
for (String rel : expectedRootLinkRels()) {
String payload = payloads.get(rel);
if (payload != null) {
Link link = assertHasLinkWithRel(rel, response);
MockHttpServletRequestBuilder request = post(link.getHref()).//
content(payload).//
contentType(MediaType.APPLICATION_JSON);
mvc.perform(request). //
andExpect(status().isCreated());
}
}
}
protected abstract Iterable<String> expectedRootLinkRels();
protected Map<String, String> getPayloadToPost() throws Exception {
return Collections.emptyMap();
}
}

View File

@@ -23,7 +23,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
@@ -72,6 +74,15 @@ public class JpaWebTests extends AbstractWebIntegrationTests {
return Arrays.asList("people");
}
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.AbstractWebIntegrationTests#getPayloadToPost()
*/
@Override
protected Map<String, String> getPayloadToPost() throws Exception {
return Collections.singletonMap("people", readFile("person.json"));
}
/**
* @see DATAREST-99
*/

View File

@@ -0,0 +1,3 @@
{ "firstName" : "Dave",
"lastName" : "Matthews"
}