DATAREST-155 - Introduce API to alter exposed backend ids.
Introduced BackendIdConverter SPI to be able to register custom components that will be used during URI construction and URI parsing to determine the actual backend identifier. To register a custom BackendIdConverter simply declare a bean definition for it in the ApplicationContext. The Spring Data REST URI creation infrastructure (RepositoryEntityLinks in particular) will pick it up transparently.
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
package org.springframework.data.rest.core.util;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Helper methods for dealing with URIs.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public abstract class UriUtils {
|
||||
|
||||
/**
|
||||
* Create a new {@link URI} out of the components.
|
||||
*
|
||||
* @param baseUri The base URI these path segments are relative to.
|
||||
* @param pathSegments The path segments to add to the given base URI.
|
||||
* @return A new URI built from the given base URI and additional path segments.
|
||||
*/
|
||||
public static URI buildUri(URI baseUri, String... pathSegments) {
|
||||
return UriComponentsBuilder.fromUri(baseUri).pathSegment(pathSegments).build().toUri();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.core.util;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests to verify that {@link UriUtils} can manipulate {@link URI}s.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class UriUtilsUnitTests {
|
||||
|
||||
private static final String BASE_URI_STR = "http://localhost:8080/data";
|
||||
private static final URI BASE_URI = URI.create(BASE_URI_STR);
|
||||
|
||||
private static final String PERSON_2LVL_STR = BASE_URI_STR + "/person/1";
|
||||
private static final URI PERSON_2LVL_URI = URI.create(PERSON_2LVL_STR);
|
||||
|
||||
@Test
|
||||
public void shouldBuildURIFromPathSegments() throws Exception {
|
||||
|
||||
URI uri = UriUtils.buildUri(BASE_URI, "person", "1");
|
||||
assertThat(uri, is(PERSON_2LVL_URI));
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.rest.webmvc;
|
||||
import static org.springframework.data.rest.core.support.DomainObjectMerger.NullHandlingPolicy.*;
|
||||
import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.SearchResourceMappings;
|
||||
import org.springframework.data.rest.core.support.DomainObjectMerger;
|
||||
import org.springframework.data.rest.core.support.DomainObjectMerger.NullHandlingPolicy;
|
||||
import org.springframework.data.rest.webmvc.support.BackendId;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
@@ -56,7 +58,6 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@@ -168,7 +169,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.POST)
|
||||
public ResponseEntity<ResourceSupport> postEntity(RootResourceInformation resourceInformation,
|
||||
public ResponseEntity<ResourceSupport> postCollectionResource(RootResourceInformation resourceInformation,
|
||||
PersistentEntityResource<?> payload, PersistentEntityResourceAssembler assembler)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
|
||||
@@ -187,7 +188,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
*/
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.GET)
|
||||
public ResponseEntity<Resource<?>> getItemResource(RootResourceInformation resourceInformation,
|
||||
@PathVariable String id, PersistentEntityResourceAssembler assembler)
|
||||
@BackendId Serializable id, PersistentEntityResourceAssembler assembler)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
|
||||
resourceInformation.verifySupportedMethod(HttpMethod.GET, ResourceType.ITEM);
|
||||
@@ -217,8 +218,8 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
* @throws HttpRequestMethodNotSupportedException
|
||||
*/
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PUT)
|
||||
public ResponseEntity<? extends ResourceSupport> putEntity(RootResourceInformation resourceInformation,
|
||||
PersistentEntityResource<Object> payload, @PathVariable String id, PersistentEntityResourceAssembler assembler)
|
||||
public ResponseEntity<? extends ResourceSupport> putItemResource(RootResourceInformation resourceInformation,
|
||||
PersistentEntityResource<Object> payload, @BackendId Serializable id, PersistentEntityResourceAssembler assembler)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
|
||||
resourceInformation.verifySupportedMethod(HttpMethod.PUT, ResourceType.ITEM);
|
||||
@@ -248,8 +249,8 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
* @throws ResourceNotFoundException
|
||||
*/
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PATCH)
|
||||
public ResponseEntity<ResourceSupport> patchEntity(RootResourceInformation resourceInformation,
|
||||
PersistentEntityResource<Object> payload, @PathVariable String id, PersistentEntityResourceAssembler assembler)
|
||||
public ResponseEntity<ResourceSupport> patchItemResource(RootResourceInformation resourceInformation,
|
||||
PersistentEntityResource<Object> payload, @BackendId Serializable id, PersistentEntityResourceAssembler assembler)
|
||||
throws HttpRequestMethodNotSupportedException, ResourceNotFoundException {
|
||||
|
||||
resourceInformation.verifySupportedMethod(HttpMethod.PATCH, ResourceType.ITEM);
|
||||
@@ -273,7 +274,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
* @throws HttpRequestMethodNotSupportedException
|
||||
*/
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.DELETE)
|
||||
public ResponseEntity<?> deleteEntity(final RootResourceInformation resourceInformation, @PathVariable final String id)
|
||||
public ResponseEntity<?> deleteItemResource(RootResourceInformation resourceInformation, @BackendId Serializable id)
|
||||
throws ResourceNotFoundException, HttpRequestMethodNotSupportedException {
|
||||
|
||||
resourceInformation.verifySupportedMethod(HttpMethod.DELETE, ResourceType.ITEM);
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.rest.webmvc;
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.data.rest.core.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMapping;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.util.Function;
|
||||
import org.springframework.data.rest.webmvc.support.BackendId;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
@@ -100,7 +102,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET)
|
||||
public ResponseEntity<ResourceSupport> followPropertyReference(final RootResourceInformation repoRequest,
|
||||
@PathVariable String id, @PathVariable String property, final PersistentEntityResourceAssembler assembler)
|
||||
@BackendId Serializable id, @PathVariable String property, final PersistentEntityResourceAssembler assembler)
|
||||
throws Exception {
|
||||
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
@@ -149,7 +151,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.DELETE)
|
||||
public ResponseEntity<? extends ResourceSupport> deletePropertyReference(final RootResourceInformation repoRequest,
|
||||
@PathVariable String id, @PathVariable String property) throws Exception {
|
||||
@BackendId Serializable id, @PathVariable String property) throws Exception {
|
||||
|
||||
final RepositoryInvoker repoMethodInvoker = repoRequest.getInvoker();
|
||||
|
||||
@@ -189,7 +191,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.GET)
|
||||
public ResponseEntity<ResourceSupport> followPropertyReference(final RootResourceInformation repoRequest,
|
||||
@PathVariable String id, @PathVariable String property, final @PathVariable String propertyId,
|
||||
@BackendId Serializable id, @PathVariable String property, final @PathVariable String propertyId,
|
||||
final PersistentEntityResourceAssembler assembler) throws Exception {
|
||||
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
@@ -242,7 +244,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = {
|
||||
"application/x-spring-data-compact+json", "text/uri-list" })
|
||||
public ResponseEntity<ResourceSupport> followPropertyReferenceCompact(RootResourceInformation repoRequest,
|
||||
@PathVariable String id, @PathVariable String property, PersistentEntityResourceAssembler assembler)
|
||||
@BackendId Serializable id, @PathVariable String property, PersistentEntityResourceAssembler assembler)
|
||||
throws Exception {
|
||||
|
||||
ResponseEntity<ResourceSupport> response = followPropertyReference(repoRequest, id, property, assembler);
|
||||
@@ -294,7 +296,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
@ResponseBody
|
||||
public ResponseEntity<? extends ResourceSupport> createPropertyReference(
|
||||
final RootResourceInformation resourceInformation, final HttpMethod requestMethod,
|
||||
final @RequestBody Resources<Object> incoming, @PathVariable String id, @PathVariable String property)
|
||||
final @RequestBody Resources<Object> incoming, @BackendId Serializable id, @PathVariable String property)
|
||||
throws Exception {
|
||||
|
||||
final RepositoryInvoker invoker = resourceInformation.getInvoker();
|
||||
@@ -372,7 +374,8 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<ResourceSupport> deletePropertyReferenceId(final RootResourceInformation repoRequest,
|
||||
@PathVariable String id, @PathVariable String property, final @PathVariable String propertyId) throws Exception {
|
||||
@BackendId Serializable id, @PathVariable String property, final @PathVariable String propertyId)
|
||||
throws Exception {
|
||||
|
||||
final RepositoryInvoker invoker = repoRequest.getInvoker();
|
||||
|
||||
@@ -434,8 +437,8 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
return conversionService.convert(id, type);
|
||||
}
|
||||
|
||||
private ResourceSupport doWithReferencedProperty(RootResourceInformation repoRequest, String id, String propertyPath,
|
||||
Function<ReferencedProperty, ResourceSupport> handler, HttpMethod method) throws Exception {
|
||||
private ResourceSupport doWithReferencedProperty(RootResourceInformation repoRequest, Serializable id,
|
||||
String propertyPath, Function<ReferencedProperty, ResourceSupport> handler, HttpMethod method) throws Exception {
|
||||
|
||||
RepositoryInvoker invoker = repoRequest.getInvoker();
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
@@ -64,6 +63,9 @@ import org.springframework.data.rest.webmvc.convert.UriListHttpMessageConverter;
|
||||
import org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter;
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter.DefaultIdConverter;
|
||||
import org.springframework.data.rest.webmvc.support.BackendIdHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.support.HttpMethodHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.support.JpaHelper;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
|
||||
@@ -86,6 +88,8 @@ import org.springframework.hateoas.hal.Jackson2HalModule.HalHandlerInstantiator;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.plugin.core.OrderAwarePluginRegistry;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
@@ -132,10 +136,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
RepositoryRestMvcConfiguration.class.getClassLoader());
|
||||
|
||||
@Autowired ListableBeanFactory beanFactory;
|
||||
@Autowired Environment environment;
|
||||
|
||||
@Autowired(required = false) List<ResourceProcessor<?>> resourceProcessors = Collections.emptyList();
|
||||
@Autowired(required = false) List<MappingContext<?, ?>> mappingContexts = Collections.emptyList();
|
||||
@Autowired(required = false) List<ResourceProcessor<?>> resourceProcessors = Collections.emptyList();
|
||||
@Autowired(required = false) List<BackendIdConverter> idConverters = Collections.emptyList();
|
||||
|
||||
@Autowired(required = false) RelProvider relProvider;
|
||||
@Autowired(required = false) CurieProvider curieProvider;
|
||||
|
||||
@@ -270,6 +275,12 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
return new ResourceMetadataHandlerMethodArgumentResolver(repositories(), resourceMappings());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BackendIdHandlerMethodArgumentResolver backendIdHandlerMethodArgumentResolver() {
|
||||
return new BackendIdHandlerMethodArgumentResolver(backendIdConverterRegistry(),
|
||||
resourceMetadataHandlerMethodArgumentResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* A special {@link org.springframework.hateoas.EntityLinks} implementation that takes repository and current
|
||||
* configuration into account when generating links.
|
||||
@@ -279,7 +290,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
*/
|
||||
@Bean
|
||||
public EntityLinks entityLinks() {
|
||||
return new RepositoryEntityLinks(repositories(), resourceMappings(), config(), pageableResolver());
|
||||
return new RepositoryEntityLinks(repositories(), resourceMappings(), config(), pageableResolver(),
|
||||
backendIdConverterRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -526,16 +538,25 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PluginRegistry<BackendIdConverter, Class<?>> backendIdConverterRegistry() {
|
||||
|
||||
List<BackendIdConverter> converters = new ArrayList<BackendIdConverter>(idConverters.size());
|
||||
converters.addAll(this.idConverters);
|
||||
converters.add(DefaultIdConverter.INSTANCE);
|
||||
|
||||
return OrderAwarePluginRegistry.create(converters);
|
||||
}
|
||||
|
||||
private List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers() {
|
||||
|
||||
PersistentEntityResourceAssemblerArgumentResolver peraResolver = new PersistentEntityResourceAssemblerArgumentResolver(
|
||||
repositories(), entityLinks(), config().projectionConfiguration(), new ProxyProjectionFactory(beanFactory));
|
||||
|
||||
return Arrays
|
||||
.asList(pageableResolver(), sortResolver(), serverHttpRequestMethodArgumentResolver(),
|
||||
repoRequestArgumentResolver(), persistentEntityArgumentResolver(),
|
||||
resourceMetadataHandlerMethodArgumentResolver(), HttpMethodHandlerMethodArgumentResolver.INSTANCE,
|
||||
peraResolver);
|
||||
return Arrays.asList(pageableResolver(), sortResolver(), serverHttpRequestMethodArgumentResolver(),
|
||||
repoRequestArgumentResolver(), persistentEntityArgumentResolver(),
|
||||
resourceMetadataHandlerMethodArgumentResolver(), HttpMethodHandlerMethodArgumentResolver.INSTANCE,
|
||||
peraResolver, backendIdHandlerMethodArgumentResolver());
|
||||
}
|
||||
|
||||
private ObjectMapper basicObjectMapper() {
|
||||
|
||||
@@ -18,19 +18,17 @@ package org.springframework.data.rest.webmvc.config;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.util.UriUtils;
|
||||
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;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* {@link HandlerMethodArgumentResolver} to create {@link ResourceMetadata} instances.
|
||||
@@ -76,32 +74,15 @@ public class ResourceMetadataHandlerMethodArgumentResolver implements HandlerMet
|
||||
public ResourceMetadata resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
String requestUri = new UrlPathHelper().getLookupPathForRequest(request);
|
||||
String repositoryKey = UriUtils.findMappingVariable("repository", parameter, webRequest);
|
||||
|
||||
if (requestUri.startsWith("/")) {
|
||||
requestUri = requestUri.substring(1);
|
||||
}
|
||||
|
||||
String[] parts = requestUri.split("/");
|
||||
|
||||
if (parts.length == 0) {
|
||||
// Root request
|
||||
return null;
|
||||
}
|
||||
|
||||
return findRepositoryInfoFor(parts[0]);
|
||||
}
|
||||
|
||||
private ResourceMetadata findRepositoryInfoFor(String pathSegment) {
|
||||
|
||||
if (!hasText(pathSegment)) {
|
||||
if (!hasText(repositoryKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Class<?> domainType : repositories) {
|
||||
ResourceMetadata mapping = mappings.getMappingFor(domainType);
|
||||
if (mapping.getPath().matches(pathSegment) && mapping.isExported()) {
|
||||
if (mapping.getPath().matches(repositoryKey) && mapping.isExported()) {
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.spi;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
|
||||
/**
|
||||
* SPI to allow the customization of how entity ids are exposed in URIs generated.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface BackendIdConverter extends Plugin<Class<?>> {
|
||||
|
||||
/**
|
||||
* Returns the id of the entity to be looked up eventually.
|
||||
*
|
||||
* @param id the source id as it was parsed from the incoming request, will never be {@literal null}.
|
||||
* @param entityType the type of the object to be resolved, will never be {@literal null}.
|
||||
* @return must not be {@literal null}.
|
||||
*/
|
||||
Serializable fromRequestId(String id, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Returns the id to be used in the URI generated to point to an entity of the given type with the given id.
|
||||
*
|
||||
* @param id the entity's id, will never be {@literal null}.
|
||||
* @param entityType the type of the entity to expose.
|
||||
* @return
|
||||
*/
|
||||
String toRequestId(Serializable id, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* The default {@link BackendIdConverter} that will simply use ids as they are.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public enum DefaultIdConverter implements BackendIdConverter {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.webmvc.support.BackendIdConverter#fromRequestId(java.lang.String, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Serializable fromRequestId(String id, Class<?> entityType) {
|
||||
return id;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.webmvc.support.BackendIdConverter#toRequestId(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public String toRequestId(Serializable id, Class<?> entityType) {
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to bind the backend id of an entity.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface BackendId {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 java.io.Serializable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.config.ResourceMetadataHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter.DefaultIdConverter;
|
||||
import org.springframework.data.rest.webmvc.util.UriUtils;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link HandlerMethodArgumentResolver} to resolve entity ids for injection int handler method arguments annotated with
|
||||
* {@link BackendId}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BackendIdHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final PluginRegistry<BackendIdConverter, Class<?>> idConverters;
|
||||
private final ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BackendIdHandlerMethodArgumentResolver} for the given {@link BackendIdConverter}s and
|
||||
* {@link ResourceMetadataHandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @param idConverters the {@link BackendIdConverter}s registered in the system.
|
||||
* @param resourceMetadataResolver the resolver to obtain {@link ResourceMetadata} from.
|
||||
*/
|
||||
public BackendIdHandlerMethodArgumentResolver(PluginRegistry<BackendIdConverter, Class<?>> idConverters,
|
||||
ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver) {
|
||||
|
||||
Assert.notNull(idConverters, "Id converters must not be null!");
|
||||
Assert.notNull(resourceMetadataResolver, "ResourceMetadata resolver must not be null!");
|
||||
|
||||
this.idConverters = idConverters;
|
||||
this.resourceMetadataResolver = resourceMetadataResolver;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(BackendId.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (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
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
|
||||
if (!parameterType.equals(Serializable.class)) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Method parameter for @%s must be of type %s! Got %s for method %s.", BackendId.class.getSimpleName(),
|
||||
Serializable.class.getSimpleName(), parameterType.getSimpleName(), parameter.getMethod()));
|
||||
}
|
||||
|
||||
ResourceMetadata metadata = resourceMetadataResolver.resolveArgument(parameter, mavContainer, request,
|
||||
binderFactory);
|
||||
|
||||
if (metadata == null) {
|
||||
throw new IllegalArgumentException("Could not obtain ResourceMetadata for request " + request);
|
||||
}
|
||||
|
||||
BackendIdConverter pluginFor = idConverters.getPluginFor(metadata.getDomainType(), DefaultIdConverter.INSTANCE);
|
||||
return pluginFor.fromRequestId(UriUtils.findMappingVariable("id", parameter, request), metadata.getDomainType());
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.springframework.data.rest.webmvc.support;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRegistration;
|
||||
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestDispatcherServlet;
|
||||
|
||||
/**
|
||||
* Helper class to HttpServletRequest helpers.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public abstract class HttpRequestUtils {
|
||||
|
||||
/**
|
||||
* Strip a servlet registration mapping from the request URI.
|
||||
*
|
||||
* @param requestUri The request URI to strip.
|
||||
* @param ctx The servlet context in which to search for registration mappings.
|
||||
* @return The stripped request URI.
|
||||
*/
|
||||
public static String stripRegistrationMapping(String requestUri, ServletContext ctx) {
|
||||
for (ServletRegistration reg : ctx.getServletRegistrations().values()) {
|
||||
if (reg.getClassName().equals(RepositoryRestDispatcherServlet.class.getName())
|
||||
|| reg.getName().equals("rest-exporter")) {
|
||||
for (String mapping : reg.getMappings()) {
|
||||
if (mapping.contains("*")) {
|
||||
mapping = mapping.substring(0, mapping.indexOf('*'));
|
||||
}
|
||||
if (requestUri.startsWith(mapping)) {
|
||||
return requestUri.replaceAll(mapping, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return requestUri;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,12 +17,16 @@ package org.springframework.data.rest.webmvc.support;
|
||||
|
||||
import static org.springframework.hateoas.TemplateVariable.VariableType.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration;
|
||||
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.rest.webmvc.spi.BackendIdConverter;
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter.DefaultIdConverter;
|
||||
import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
@@ -31,6 +35,7 @@ import org.springframework.hateoas.TemplateVariable;
|
||||
import org.springframework.hateoas.TemplateVariables;
|
||||
import org.springframework.hateoas.UriTemplate;
|
||||
import org.springframework.hateoas.core.AbstractEntityLinks;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
@@ -48,6 +53,7 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
private final ResourceMappings mappings;
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final HateoasPageableHandlerMethodArgumentResolver resolver;
|
||||
private final PluginRegistry<BackendIdConverter, Class<?>> idConverters;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RepositoryEntityLinks}.
|
||||
@@ -56,20 +62,24 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
* @param mappings must not be {@literal null}.
|
||||
* @param config must not be {@literal null}.
|
||||
* @param resolver must not be {@literal null}.
|
||||
* @param idConverters must not be {@literal null}.
|
||||
*/
|
||||
@Autowired
|
||||
public RepositoryEntityLinks(Repositories repositories, ResourceMappings mappings,
|
||||
RepositoryRestConfiguration config, HateoasPageableHandlerMethodArgumentResolver resolver) {
|
||||
RepositoryRestConfiguration config, HateoasPageableHandlerMethodArgumentResolver resolver,
|
||||
PluginRegistry<BackendIdConverter, Class<?>> idConverters) {
|
||||
|
||||
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!");
|
||||
Assert.notNull(idConverters, "Id converter registry must not be null!");
|
||||
|
||||
this.repositories = repositories;
|
||||
this.mappings = mappings;
|
||||
this.config = config;
|
||||
this.resolver = resolver;
|
||||
this.idConverters = idConverters;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -134,8 +144,12 @@ public class RepositoryEntityLinks extends AbstractEntityLinks {
|
||||
@Override
|
||||
public Link linkToSingleResource(Class<?> type, Object id) {
|
||||
|
||||
Assert.isInstanceOf(Serializable.class, id, "Id must be assignable to Serializable!");
|
||||
|
||||
ResourceMetadata metadata = mappings.getMappingFor(type);
|
||||
Link link = linkFor(type).slash(id).withRel(metadata.getItemResourceRel());
|
||||
String mappedId = idConverters.getPluginFor(type, DefaultIdConverter.INSTANCE).toRequestId((Serializable) id, type);
|
||||
|
||||
Link link = linkFor(type).slash(mappedId).withRel(metadata.getItemResourceRel());
|
||||
ProjectionDefinitionConfiguration projectionConfiguration = config.projectionConfiguration();
|
||||
|
||||
if (!projectionConfiguration.hasProjectionFor(type)) {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.hateoas.UriTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Utility methods to work with requests and URIs.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class UriUtils {
|
||||
|
||||
private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();
|
||||
|
||||
private UriUtils() {}
|
||||
|
||||
/**
|
||||
* Returns the value for the mapping variable with the given name.
|
||||
*
|
||||
* @param variable must not be {@literal null} or empty.
|
||||
* @param parameter
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static String findMappingVariable(String variable, MethodParameter parameter, NativeWebRequest request) {
|
||||
|
||||
Assert.hasText(variable, "Variable name must not be null or empty!");
|
||||
Assert.notNull(parameter, "Method parameter must not be null!");
|
||||
Assert.notNull(request, "Request must not be null!");
|
||||
|
||||
String lookupPath = getCleanLookupPath(request);
|
||||
RequestMapping annotation = parameter.getMethodAnnotation(RequestMapping.class);
|
||||
|
||||
for (String mapping : annotation.value()) {
|
||||
|
||||
Map<String, String> variables = new org.springframework.web.util.UriTemplate(mapping).match(lookupPath);
|
||||
String value = variables.get(variable);
|
||||
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getCleanLookupPath(NativeWebRequest request) {
|
||||
|
||||
HttpServletRequest httpServletRequest = request.getNativeRequest(HttpServletRequest.class);
|
||||
String lookupPath = URL_PATH_HELPER.getLookupPathForRequest(httpServletRequest);
|
||||
return new UriTemplate(lookupPath).expand().toString();
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,6 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll
|
||||
|
||||
RootResourceInformation request = getResourceInformation(Address.class);
|
||||
|
||||
controller.postEntity(request, null, null);
|
||||
controller.postCollectionResource(request, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-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.
|
||||
@@ -20,13 +20,15 @@ import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
|
||||
@Entity
|
||||
public class Book {
|
||||
|
||||
@Id String isbn;
|
||||
@Id @GeneratedValue Long id;
|
||||
String isbn;
|
||||
|
||||
@ManyToMany(cascade = { CascadeType.MERGE })//
|
||||
Set<Author> authors;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link BackendIdConverter} artificially transforming the actual book id into some magic {@link String} and back.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BookIdConverter implements BackendIdConverter {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.webmvc.support.BackendIdConverter#fromRequestId(java.lang.String, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Serializable fromRequestId(String id, Class<?> entityType) {
|
||||
return Long.parseLong(id.substring(0, id.indexOf('-')));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.webmvc.support.BackendIdConverter#toRequestId(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public String toRequestId(Serializable id, Class<?> entityType) {
|
||||
|
||||
Long longId = (Long) id;
|
||||
List<Long> ids = new ArrayList<Long>(longId.intValue());
|
||||
|
||||
for (int i = 0; i < longId; i++) {
|
||||
ids.add(longId);
|
||||
}
|
||||
|
||||
return StringUtils.collectionToDelimitedString(ids, "-");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return Book.class.equals(delimiter);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,6 @@ import org.springframework.data.repository.CrudRepository;
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface BookRepository extends CrudRepository<Book, String> {
|
||||
public interface BookRepository extends CrudRepository<Book, Long> {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-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.
|
||||
@@ -21,6 +21,8 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* Test configuration for JPA.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -29,6 +31,11 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
@EnableTransactionManagement
|
||||
public class JpaRepositoryConfig extends JpaInfrastructureConfig {
|
||||
|
||||
@Bean
|
||||
public BookIdConverter bookIdConverter() {
|
||||
return new BookIdConverter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestDataPopulator testDataPopulator() {
|
||||
return new TestDataPopulator();
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.rest.webmvc.AbstractControllerIntegrationTests;
|
||||
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link BackendIdHandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration(classes = JpaRepositoryConfig.class)
|
||||
public class BackendIdConverterHandlerMethodArgumentResolverIntegrationTests extends AbstractControllerIntegrationTests {
|
||||
|
||||
@Autowired BackendIdHandlerMethodArgumentResolver resolver;
|
||||
|
||||
/**
|
||||
* @see DATAREST-267, DATAREST-268
|
||||
*/
|
||||
@Test
|
||||
public void stripsUriTemplateVariablesFromUri() throws Exception {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(SampleController.class, "resolveId", Serializable.class);
|
||||
MethodParameter parameter = new MethodParameter(method, 0);
|
||||
NativeWebRequest request = new ServletWebRequest(new MockHttpServletRequest("GET", "/orders/5{?projection}"));
|
||||
|
||||
Object resolvedId = resolver.resolveArgument(parameter, null, request, null);
|
||||
|
||||
assertThat(resolvedId, is((Object) "5"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-155
|
||||
*/
|
||||
@Test
|
||||
public void translatesUriToBackendId() throws Exception {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(SampleController.class, "resolveId", Serializable.class);
|
||||
MethodParameter parameter = new MethodParameter(method, 0);
|
||||
NativeWebRequest request = new ServletWebRequest(new MockHttpServletRequest("GET", "/books/5-5-5-5-5"));
|
||||
|
||||
Object resolvedId = resolver.resolveArgument(parameter, null, request, null);
|
||||
|
||||
assertThat(resolvedId, is((Object) 5L));
|
||||
}
|
||||
|
||||
static class SampleController {
|
||||
|
||||
@RequestMapping("/{repository}/{id}")
|
||||
void resolveId(@BackendId Serializable backendId) {}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.webmvc.AbstractControllerIntegrationTests;
|
||||
import org.springframework.data.rest.webmvc.jpa.Book;
|
||||
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.data.rest.webmvc.jpa.Order;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
@@ -69,4 +70,14 @@ public class RepositoryEntityLinksIntegrationTests extends AbstractControllerInt
|
||||
assertThat(link.isTemplated(), is(true));
|
||||
assertThat(link.getVariableNames(), hasItem(configuration.projectionConfiguration().getParameterName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-155
|
||||
*/
|
||||
@Test
|
||||
public void usesCustomGeneratedBackendId() {
|
||||
|
||||
Link link = entityLinks.linkToSingleResource(Book.class, 7L);
|
||||
assertThat(link.getHref(), endsWith("/7-7-7-7-7-7-7"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user