DATAREST-229 - RepositoryEntityLinks now exposes templated links.

If the resource exposed for a domain or repository type is considered a paging resource we now return a templated URI. Introduced ResourceMapping.isPagingResource() to allow clients to find out about whether the resource is actually capable of pagination. Adapted implementations to inspect the findAll(…) method as well as the search methods for a Pageable parameter.

Tweaked pom.xml to create correct classpaths if the IDE uses direct workspace project references.
This commit is contained in:
Oliver Gierke
2014-01-22 17:54:35 +01:00
parent 5778ff9940
commit 88b2f05edf
21 changed files with 309 additions and 313 deletions

View File

@@ -244,7 +244,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
*/
@Bean
public EntityLinks entityLinks() {
return new RepositoryEntityLinks(repositories(), resourceMappings(), config());
return new RepositoryEntityLinks(repositories(), resourceMappings(), config(), pageableResolver());
}
/**

View File

@@ -5,12 +5,21 @@ import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.mapping.ResourceMappings;
import org.springframework.data.rest.core.mapping.ResourceMetadata;
import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkBuilder;
import org.springframework.hateoas.TemplateVariables;
import org.springframework.hateoas.UriTemplate;
import org.springframework.hateoas.core.AbstractEntityLinks;
import org.springframework.util.Assert;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
/**
* {@link EntityLinks} implementation that is able to create {@link Link} for domain classes managed by Spring Data
* REST.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
@@ -19,15 +28,29 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
private final Repositories repositories;
private final ResourceMappings mappings;
private final RepositoryRestConfiguration config;
private final HateoasPageableHandlerMethodArgumentResolver resolver;
/**
* Creates a new {@link RepositoryEntityLinks}.
*
* @param repositories must not be {@literal null}.
* @param mappings must not be {@literal null}.
* @param config must not be {@literal null}.
* @param resolver must not be {@literal null}.
*/
@Autowired
public RepositoryEntityLinks(Repositories repositories, ResourceMappings mappings, RepositoryRestConfiguration config) {
public RepositoryEntityLinks(Repositories repositories, ResourceMappings mappings,
RepositoryRestConfiguration config, HateoasPageableHandlerMethodArgumentResolver resolver) {
Assert.notNull(repositories, "Repositories must not be null!");
Assert.notNull(mappings, "ResourceMappings must not be null!");
Assert.notNull(config, "RepositoryRestConfiguration must not be null!");
Assert.notNull(resolver, "HateoasPageableHandlerMethodArgumentResolver must not be null!");
this.repositories = repositories;
this.mappings = mappings;
this.config = config;
this.resolver = resolver;
}
/*
@@ -67,6 +90,17 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
public Link linkToCollectionResource(Class<?> type) {
ResourceMetadata metadata = mappings.getMappingFor(type);
if (metadata.isPagingResource()) {
Link link = linkFor(type).withSelfRel();
String href = link.getHref();
UriComponents components = UriComponentsBuilder.fromUriString(href).build();
TemplateVariables variables = resolver.getPaginationTemplateVariables(null, components);
return new Link(new UriTemplate(href, variables), metadata.getRel());
}
return linkFor(type).withRel(metadata.getRel());
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.rest.webmvc;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mapping.PersistentEntity;
@@ -47,6 +48,11 @@ public abstract class AbstractControllerIntegrationTests {
@Autowired RepositoryInvokerFactory invokerFactory;
@Autowired ResourceMappings mappings;
@Before
public void initWebInfrastructure() {
WebTestUtils.initWebTest();
}
/**
* Returns a {@link RepositoryRestRequest} for the given domain type.
*

View File

@@ -87,7 +87,7 @@ public abstract class AbstractWebIntegrationTests {
}
protected MockHttpServletResponse request(Link link) throws Exception {
return request(link.getHref());
return request(link.expand().getHref());
}
protected MockHttpServletResponse request(String href) throws Exception {
@@ -95,7 +95,7 @@ public abstract class AbstractWebIntegrationTests {
}
protected ResultActions follow(Link link) throws Exception {
return follow(link.getHref());
return follow(link.expand().getHref());
}
protected ResultActions follow(String href) throws Exception {
@@ -264,7 +264,7 @@ public abstract class AbstractWebIntegrationTests {
request(link);
// Schema - TODO:Improve by using hypermedia
mvc.perform(get(link.getHref() + "/schema").//
mvc.perform(get(link.expand().getHref() + "/schema").//
accept(MediaType.parseMediaType("application/schema+json"))).//
andExpect(status().isOk());
}
@@ -313,7 +313,7 @@ public abstract class AbstractWebIntegrationTests {
}
@Test
public void postsPayloadToResource() throws Exception {
public void nic() throws Exception {
Map<String, String> payloads = getPayloadToPost();
assumeFalse(payloads.isEmpty());
@@ -325,9 +325,11 @@ public abstract class AbstractWebIntegrationTests {
String payload = payloads.get(rel);
if (payload != null) {
Link link = assertHasLinkWithRel(rel, response);
MockHttpServletRequestBuilder request = post(link.getHref()).//
Link link = assertHasLinkWithRel(rel, response);
String target = link.expand().getHref();
MockHttpServletRequestBuilder request = post(target).//
content(payload).//
contentType(MediaType.APPLICATION_JSON);

View File

@@ -0,0 +1,38 @@
/*
* 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.rest.webmvc;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 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);
}
}

View File

@@ -48,6 +48,7 @@ public class JpaRepositoryConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setGenerateDdl(true);
@@ -55,6 +56,7 @@ public class JpaRepositoryConfig {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
factory.setPersistenceUnitName("spring-data-rest-webmvc");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2014 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.support;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.AbstractControllerIntegrationTests;
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
import org.springframework.data.rest.webmvc.jpa.Person;
import org.springframework.hateoas.Link;
import org.springframework.test.context.ContextConfiguration;
/**
* Integration tests for {@link RepositoryEntityLinks}.
*
* @author Oliver Gierke
*/
@ContextConfiguration(classes = JpaRepositoryConfig.class)
public class RepositoryEntityLinksIntegrationTests extends AbstractControllerIntegrationTests {
@Autowired RepositoryEntityLinks entityLinks;
@Test
public void returnsLinkToSingleResource() {
Link link = entityLinks.linkToSingleResource(Person.class, 1);
assertThat(link.getHref(), endsWith("/people/1"));
assertThat(link.getRel(), is("person"));
}
@Test
public void returnsTemplatedLinkForPagingResource() {
Link link = entityLinks.linkToCollectionResource(Person.class);
assertThat(link.isTemplated(), is(true));
assertThat(link.getVariableNames(), hasItems("page", "size", "sort"));
assertThat(link.getRel(), is("people"));
}
}