DATACMNS-330, DATACMNS-331 - Advanced web integration.
Added PagedResourcesAssembler to easily convert Page instances into a PagedResource instance and automatically build the required previous/next links based on the PageableHandlerMethodArgumentResolver present in the MVC configuration. The assembler can either be injected into a Spring MVC controller or a controller method. The latter will then assume the controller methods URI to be used as pagination link base. Added @EnableSpringDataWebSupport to automatically register HandlerMethodArgumentResolvers for Pageable and Sort as well as a DomainClassConverter that will allow the usage of domain types being managed by Spring Data repositories in controller method signatures. In case Spring HATEOAS is present on the classpath, we'll also register a PagedResourcesAssembler for injection as well as the appropriate HandlerMethodArgumentResolver for injection into controller methods. Adapted Sonargraph configuration accordingly. Upgraded to Spring HATEOAS 0.6.0.BUILD-SNAPSHOT.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 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.web;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.PagedResources;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PagedResourcesAssembler}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PagedResourcesAssemblerUnitTests {
|
||||
|
||||
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
WebTestUtils.initWebTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addsNextLinkForFirstPage() {
|
||||
|
||||
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<Person>(resolver, null);
|
||||
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(0));
|
||||
|
||||
assertThat(resources.getLink(Link.REL_PREVIOUS), is(nullValue()));
|
||||
assertThat(resources.getLink(Link.REL_NEXT), is(notNullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addsPreviousAndNextLinksForMiddlePage() {
|
||||
|
||||
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<Person>(resolver, null);
|
||||
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1));
|
||||
|
||||
assertThat(resources.getLink(Link.REL_PREVIOUS), is(notNullValue()));
|
||||
assertThat(resources.getLink(Link.REL_NEXT), is(notNullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addsPreviousLinkForLastPage() {
|
||||
|
||||
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<Person>(resolver, null);
|
||||
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(2));
|
||||
|
||||
assertThat(resources.getLink(Link.REL_PREVIOUS), is(notNullValue()));
|
||||
assertThat(resources.getLink(Link.REL_NEXT), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesBaseUriIfConfigured() {
|
||||
|
||||
UriComponents baseUri = UriComponentsBuilder.fromUriString("http://foo:9090").build();
|
||||
|
||||
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<Person>(resolver, baseUri);
|
||||
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1));
|
||||
|
||||
assertThat(resources.getLink(Link.REL_PREVIOUS).getHref(), startsWith(baseUri.toUriString()));
|
||||
assertThat(resources.getLink(Link.REL_NEXT).getHref(), startsWith(baseUri.toUriString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesCustomLinkProvided() {
|
||||
|
||||
Link link = new Link("http://foo:9090", "rel");
|
||||
|
||||
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<Person>(resolver, null);
|
||||
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1), link);
|
||||
|
||||
assertThat(resources.getLink(Link.REL_PREVIOUS).getHref(), startsWith(link.getHref()));
|
||||
assertThat(resources.getLink(Link.REL_NEXT).getHref(), startsWith(link.getHref()));
|
||||
}
|
||||
|
||||
private static Page<Person> createPage(int index) {
|
||||
|
||||
PageRequest request = new PageRequest(index, 1);
|
||||
return new PageImpl<Person>(Arrays.asList(new Person()), request, 3);
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
}
|
||||
}
|
||||
61
src/test/java/org/springframework/data/web/WebTestUtils.java
Normal file
61
src/test/java/org/springframework/data/web/WebTestUtils.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 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.web;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
/**
|
||||
* Helper methods for web integration testing.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class WebTestUtils {
|
||||
|
||||
/**
|
||||
* Initializes web tests. Will register a {@link MockHttpServletRequest} for the current thread.
|
||||
*/
|
||||
public static void initWebTest() {
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
|
||||
RequestContextHolder.setRequestAttributes(requestAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link WebApplicationContext} from the given configuration classes.
|
||||
*
|
||||
* @param configClasses
|
||||
* @return
|
||||
*/
|
||||
public static WebApplicationContext createApplicationContext(Class<?>... configClasses) {
|
||||
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(new MockServletContext());
|
||||
|
||||
for (Class<?> configClass : configClasses) {
|
||||
context.register(configClass);
|
||||
}
|
||||
|
||||
context.refresh();
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 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.web.config;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.web.PagedResourcesAssemblerArgumentResolver;
|
||||
import org.springframework.data.web.SortHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.web.WebTestUtils;
|
||||
import org.springframework.data.web.config.EnableSpringDataWebSupport.SpringDataWebConfigurationImportSelector;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link EnableSpringDataWebSupport}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @see DATACMNS-330
|
||||
*/
|
||||
public class EnableSpringDataWebSupportIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@EnableSpringDataWebSupport
|
||||
static class SampleConfig {
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
reEnableHateoas();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registersBasicBeanDefinitions() throws Exception {
|
||||
|
||||
ApplicationContext context = WebTestUtils.createApplicationContext(SampleConfig.class);
|
||||
List<String> names = Arrays.asList(context.getBeanDefinitionNames());
|
||||
|
||||
assertThat(names, hasItems("pageableResolver", "sortResolver", "mvcDomainClassConverter"));
|
||||
|
||||
assertResolversRegistered(context, SortHandlerMethodArgumentResolver.class,
|
||||
PageableHandlerMethodArgumentResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registersHateoasSpecificBeanDefinitions() throws Exception {
|
||||
|
||||
ApplicationContext context = WebTestUtils.createApplicationContext(SampleConfig.class);
|
||||
List<String> names = Arrays.asList(context.getBeanDefinitionNames());
|
||||
|
||||
assertThat(names, hasItems("pagedResourcesAssembler", "pagedResourcesAssemblerArgumentResolver"));
|
||||
assertResolversRegistered(context, PagedResourcesAssemblerArgumentResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotRegisterHateoasSpecificComponentsIfHateoasNotPresent() throws Exception {
|
||||
|
||||
hideHateoas();
|
||||
|
||||
ApplicationContext context = WebTestUtils.createApplicationContext(SampleConfig.class);
|
||||
List<String> names = Arrays.asList(context.getBeanDefinitionNames());
|
||||
|
||||
assertThat(names, not(hasItems("pagedResourcesAssembler", "pagedResourcesAssemblerArgumentResolver")));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void assertResolversRegistered(ApplicationContext context, Class<?>... resolverTypes) {
|
||||
|
||||
RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
|
||||
assertThat(adapter, is(notNullValue()));
|
||||
List<HandlerMethodArgumentResolver> resolvers = adapter.getCustomArgumentResolvers();
|
||||
|
||||
List<Matcher<Object>> resolverMatchers = new ArrayList<Matcher<Object>>(resolverTypes.length);
|
||||
|
||||
for (Class<?> resolverType : resolverTypes) {
|
||||
resolverMatchers.add(instanceOf(resolverType));
|
||||
}
|
||||
|
||||
assertThat(resolvers, hasItems(resolverMatchers.toArray(new Matcher[resolverMatchers.size()])));
|
||||
}
|
||||
|
||||
private static void hideHateoas() throws Exception {
|
||||
|
||||
Field field = ReflectionUtils.findField(SpringDataWebConfigurationImportSelector.class, "HATEOAS_PRESENT");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, null, false);
|
||||
}
|
||||
|
||||
private static void reEnableHateoas() {
|
||||
|
||||
Field field = ReflectionUtils.findField(SpringDataWebConfigurationImportSelector.class, "HATEOAS_PRESENT");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, null, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 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.web.config;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.data.web.WebTestUtils;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.PagedResources;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link PagedResourcesAssembler}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PageableResourcesAssemblerIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@EnableSpringDataWebSupport
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public SampleController controller() {
|
||||
return new SampleController();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
WebTestUtils.initWebTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void injectsPagedResourcesAssembler() {
|
||||
|
||||
WebApplicationContext context = WebTestUtils.createApplicationContext(Config.class);
|
||||
SampleController controller = context.getBean(SampleController.class);
|
||||
|
||||
assertThat(controller.assembler, is(notNullValue()));
|
||||
|
||||
PagedResources<Resource<Person>> resources = controller.sample(new PageRequest(1, 1));
|
||||
|
||||
assertThat(resources.getLink(Link.REL_PREVIOUS), is(notNullValue()));
|
||||
assertThat(resources.getLink(Link.REL_NEXT), is(notNullValue()));
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class SampleController {
|
||||
|
||||
@Autowired
|
||||
PagedResourcesAssembler<Person> assembler;
|
||||
|
||||
@RequestMapping("/persons")
|
||||
PagedResources<Resource<Person>> sample(Pageable pageable) {
|
||||
|
||||
Page<Person> page = new PageImpl<Person>(Arrays.asList(new Person()), pageable, pageable.getOffset()
|
||||
+ pageable.getPageSize() + 1);
|
||||
|
||||
return assembler.toResource(page);
|
||||
}
|
||||
}
|
||||
|
||||
static class Person {
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user