DATAREST-93 - More cleanups.
Merged core and repository modules into core. Renamed some packages for consistency in naming and in preparation to break up some package cycles. Removed @BaseUri and the according resolver. Refactored controllers a bit to have more reusable chunks of code.
This commit is contained in:
@@ -33,8 +33,8 @@ import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.rest.repository.RepositoryConstraintViolationException;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.support.ExceptionMessage;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage;
|
||||
import org.springframework.data.rest.webmvc.support.ValidationExceptionHandler;
|
||||
|
||||
@@ -1,60 +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.webmvc;
|
||||
|
||||
import java.net.URI;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.webmvc.annotation.BaseURI;
|
||||
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.servlet.support.ServletUriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BaseUriMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final RepositoryRestConfiguration config;
|
||||
|
||||
public BaseUriMethodArgumentResolver(RepositoryRestConfiguration config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return (null != parameter.getParameterAnnotation(BaseURI.class) && parameter.getParameterType() == URI.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
|
||||
// Use configured URI if there is one or set the current one as the default if not.
|
||||
if (null == config.getBaseUri()) {
|
||||
URI baseUri = ServletUriComponentsBuilder.fromServletMapping(servletRequest).build().toUri();
|
||||
config.setBaseUri(baseUri);
|
||||
}
|
||||
|
||||
return config.getBaseUri();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import java.util.Collections;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -35,14 +36,14 @@ public class ControllerUtils {
|
||||
public static final Iterable<Resource<?>> EMPTY_RESOURCE_LIST = Collections.emptyList();
|
||||
public static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
|
||||
|
||||
public static <R extends Resource<?>> ResponseEntity<Resource<?>> toResponseEntity(HttpHeaders headers, R resource,
|
||||
HttpStatus status) {
|
||||
public static <R extends ResourceSupport> ResponseEntity<ResourceSupport> toResponseEntity(HttpHeaders headers,
|
||||
R resource, HttpStatus status) {
|
||||
|
||||
HttpHeaders hdrs = new HttpHeaders();
|
||||
if (null != headers) {
|
||||
hdrs.putAll(headers);
|
||||
}
|
||||
|
||||
return new ResponseEntity<Resource<?>>(resource, hdrs, status);
|
||||
return new ResponseEntity<ResourceSupport>(resource, hdrs, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.Arrays;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
|
||||
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
|
||||
*/
|
||||
public class PersistentEntityResource<T> extends Resource<T> {
|
||||
|
||||
@JsonIgnore private final PersistentEntity<?, ?> entity;
|
||||
|
||||
public static <T> PersistentEntityResource<T> wrap(PersistentEntity<?, ?> entity, T obj) {
|
||||
return new PersistentEntityResource<T>(entity, obj);
|
||||
}
|
||||
|
||||
public PersistentEntityResource(PersistentEntity<?, ?> entity, T content, Link... links) {
|
||||
this(entity, content, Arrays.asList(links));
|
||||
}
|
||||
|
||||
private PersistentEntityResource(PersistentEntity<?, ?> entity, T content, Iterable<Link> links) {
|
||||
super(content, links);
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public PersistentEntity<?, ?> getPersistentEntity() {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.springframework.data.rest.webmvc;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.ResourceAssembler;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
|
||||
@@ -17,8 +17,8 @@ package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@@ -34,23 +34,23 @@ import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.context.AfterCreateEvent;
|
||||
import org.springframework.data.rest.repository.context.AfterDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.AfterSaveEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeCreateEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeSaveEvent;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.repository.mapping.SearchResourceMappings;
|
||||
import org.springframework.data.rest.repository.support.DomainObjectMerger;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.core.event.AfterCreateEvent;
|
||||
import org.springframework.data.rest.core.event.AfterDeleteEvent;
|
||||
import org.springframework.data.rest.core.event.AfterSaveEvent;
|
||||
import org.springframework.data.rest.core.event.BeforeCreateEvent;
|
||||
import org.springframework.data.rest.core.event.BeforeDeleteEvent;
|
||||
import org.springframework.data.rest.core.event.BeforeSaveEvent;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvoker;
|
||||
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.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.PagedResources;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -160,7 +160,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.POST, consumes = { "application/json" }, produces = {
|
||||
"application/json", "text/uri-list" })
|
||||
public ResponseEntity<Resource<?>> createNewEntity(RepositoryRestRequest repoRequest,
|
||||
public ResponseEntity<ResourceSupport> createNewEntity(RepositoryRestRequest repoRequest,
|
||||
PersistentEntityResource<?> incoming) {
|
||||
|
||||
RepositoryInvoker invoker = repoRequest.getRepositoryInvoker();
|
||||
@@ -222,7 +222,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PUT, consumes = { "application/json" },
|
||||
produces = { "application/json", "text/uri-list" })
|
||||
public ResponseEntity<Resource<?>> updateEntity(RepositoryRestRequest request,
|
||||
public ResponseEntity<? extends ResourceSupport> updateEntity(RepositoryRestRequest request,
|
||||
PersistentEntityResource<Object> incoming, @PathVariable String id) {
|
||||
|
||||
RepositoryInvoker invoker = request.getRepositoryInvoker();
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvokerFactory;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvokerFactory;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.hateoas.Resources;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.hateoas.Resources;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@@ -35,19 +34,20 @@ import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.config.ResourceMapping;
|
||||
import org.springframework.data.rest.core.event.AfterLinkDeleteEvent;
|
||||
import org.springframework.data.rest.core.event.AfterLinkSaveEvent;
|
||||
import org.springframework.data.rest.core.event.BeforeLinkDeleteEvent;
|
||||
import org.springframework.data.rest.core.event.BeforeLinkSaveEvent;
|
||||
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.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.context.AfterLinkDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.AfterLinkSaveEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeLinkDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeLinkSaveEvent;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -64,29 +64,26 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RestController
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class RepositoryPropertyReferenceController extends AbstractRepositoryRestController implements
|
||||
ApplicationEventPublisherAware {
|
||||
|
||||
private static final String BASE_MAPPING = "/{repository}/{id}/{property}";
|
||||
|
||||
private final Repositories repositories;
|
||||
private final RepositoryRestConfiguration config;
|
||||
private final PersistentEntityResourceAssembler<Object> perAssembler;
|
||||
private final DomainClassConverter<?> converter;
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Autowired
|
||||
public RepositoryPropertyReferenceController(Repositories repositories, RepositoryRestConfiguration config,
|
||||
DomainClassConverter<?> domainClassConverter, PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
public RepositoryPropertyReferenceController(Repositories repositories, DomainClassConverter<?> domainClassConverter,
|
||||
PagedResourcesAssembler<Object> assembler, PersistentEntityResourceAssembler<Object> perAssembler) {
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
this.repositories = repositories;
|
||||
this.perAssembler = perAssembler;
|
||||
this.config = config;
|
||||
this.converter = domainClassConverter;
|
||||
}
|
||||
|
||||
@@ -102,12 +99,15 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-verbose+json" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> followPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
public ResponseEntity<ResourceSupport> followPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException {
|
||||
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
|
||||
Function<ReferencedProperty, ResourceSupport> handler = new Function<ReferencedProperty, ResourceSupport>() {
|
||||
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
public ResourceSupport apply(ReferencedProperty prop) {
|
||||
|
||||
if (null == prop.propertyValue) {
|
||||
throw new ResourceNotFoundException();
|
||||
@@ -121,7 +121,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
resources.add(perAssembler.toResource(obj));
|
||||
}
|
||||
|
||||
return new Resource<Object>(resources);
|
||||
return new Resources<Resource<?>>(resources);
|
||||
|
||||
} else if (prop.property.isMap()) {
|
||||
|
||||
@@ -141,13 +141,14 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
}
|
||||
}
|
||||
};
|
||||
Resource<?> responseResource = doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
|
||||
ResourceSupport responseResource = doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
return ControllerUtils.toResponseEntity(headers, responseResource, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> deletePropertyReference(final RepositoryRestRequest repoRequest,
|
||||
public ResponseEntity<? extends ResourceSupport> deletePropertyReference(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException,
|
||||
HttpRequestMethodNotSupportedException {
|
||||
|
||||
@@ -157,12 +158,15 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
return new ResponseEntity<Resource<?>>(HttpStatus.METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
Function<ReferencedProperty, ResourceSupport> handler = new Function<ReferencedProperty, ResourceSupport>() {
|
||||
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
|
||||
if (null == prop.propertyValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (prop.property.isCollectionLike()) {
|
||||
throw new IllegalArgumentException(new HttpRequestMethodNotSupportedException("DELETE"));
|
||||
} else if (prop.property.isMap()) {
|
||||
@@ -174,6 +178,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
publisher.publishEvent(new BeforeLinkDeleteEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
Object result = repoMethodInvoker.invokeSave(prop.wrapper.getBean());
|
||||
publisher.publishEvent(new AfterLinkDeleteEvent(result, prop.propertyValue));
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -188,16 +193,19 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-verbose+json", "application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> followPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
public ResponseEntity<ResourceSupport> followPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id, @PathVariable String property, final @PathVariable String propertyId)
|
||||
throws ResourceNotFoundException, NoSuchMethodException {
|
||||
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
Function<ReferencedProperty, ResourceSupport> handler = new Function<ReferencedProperty, ResourceSupport>() {
|
||||
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
public ResourceSupport apply(ReferencedProperty prop) {
|
||||
|
||||
if (null == prop.propertyValue) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
@@ -234,46 +242,54 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
}
|
||||
};
|
||||
|
||||
Resource<?> responseResource = doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
ResourceSupport responseResource = doWithReferencedProperty(repoRequest, id, property, handler);
|
||||
return ControllerUtils.toResponseEntity(headers, responseResource, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = {
|
||||
"application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> followPropertyReferenceCompact(RepositoryRestRequest repoRequest,
|
||||
public ResponseEntity<ResourceSupport> followPropertyReferenceCompact(RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id, @PathVariable String property) throws ResourceNotFoundException, NoSuchMethodException {
|
||||
ResponseEntity<Resource<?>> response = followPropertyReference(repoRequest, id, property);
|
||||
|
||||
ResponseEntity<ResourceSupport> response = followPropertyReference(repoRequest, id, property);
|
||||
|
||||
if (response.getStatusCode() != HttpStatus.OK) {
|
||||
return response;
|
||||
}
|
||||
|
||||
ResourceMetadata repoMapping = repoRequest.getResourceMetadata();
|
||||
PersistentProperty<?> persistentProp = repoRequest.getPersistentEntity().getPersistentProperty(property);
|
||||
ResourceMapping propertyMapping = repoMapping.getMappingFor(persistentProp);
|
||||
|
||||
Class<?> propType = persistentProp.isCollectionLike() || persistentProp.isMap() ? persistentProp.getComponentType()
|
||||
: persistentProp.getType();
|
||||
ResourceMapping propRepoMapping = getResourceMapping(config, repositories.getRepositoryInformationFor(propType));
|
||||
String propRel = String.format("%s.%s.%s", repoMapping.getSingleResourceRel(), property, propRepoMapping.getRel());
|
||||
|
||||
Resource<?> resource = response.getBody();
|
||||
ResourceSupport resource = response.getBody();
|
||||
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
URI entityBaseUri = buildUri(repoRequest.getBaseUri(), repoMapping.getPath().toString(), id, property);
|
||||
ControllerLinkBuilder linkBuilder = linkTo(methodOn(RepositoryPropertyReferenceController.class)
|
||||
.followPropertyReference(repoRequest, id, property));
|
||||
|
||||
if (resource.getContent() instanceof Iterable) {
|
||||
for (Resource<?> res : (Iterable<Resource<?>>) resource.getContent()) {
|
||||
Link propLink = propertyReferenceLink(res, entityBaseUri, propRel);
|
||||
links.add(propLink);
|
||||
}
|
||||
} else if (resource.getContent() instanceof Map) {
|
||||
for (Map.Entry<Object, Resource<?>> entry : ((Map<Object, Resource<?>>) resource.getContent()).entrySet()) {
|
||||
Link l = new Link(entry.getValue().getLink("self").getHref(), entry.getKey().toString());
|
||||
links.add(l);
|
||||
if (resource instanceof Resource) {
|
||||
|
||||
Object content = ((Resource<?>) resource).getContent();
|
||||
if (content instanceof Iterable) {
|
||||
|
||||
for (Resource<?> res : (Iterable<Resource<?>>) content) {
|
||||
links.add(linkBuilder.withRel(propertyMapping.getRel()));
|
||||
}
|
||||
|
||||
} else if (content instanceof Map) {
|
||||
|
||||
Map<Object, Resource<?>> map = (Map<Object, Resource<?>>) content;
|
||||
|
||||
for (Entry<Object, Resource<?>> entry : map.entrySet()) {
|
||||
Link l = new Link(entry.getValue().getLink("self").getHref(), entry.getKey().toString());
|
||||
links.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
links.add(new Link(entityBaseUri.toString(), propRel));
|
||||
links.add(linkBuilder.withRel(propertyMapping.getRel()));
|
||||
}
|
||||
|
||||
return ControllerUtils.toResponseEntity(null, new Resource<Object>(EMPTY_RESOURCE_LIST, links), HttpStatus.OK);
|
||||
@@ -282,7 +298,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
@RequestMapping(value = BASE_MAPPING, method = { RequestMethod.POST, RequestMethod.PUT }, consumes = {
|
||||
"application/json", "application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> createPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
public ResponseEntity<? extends ResourceSupport> createPropertyReference(final RepositoryRestRequest repoRequest,
|
||||
final @RequestBody Resource<Object> incoming, @PathVariable String id, @PathVariable String property)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
@@ -291,38 +307,53 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
if (!invoker.exposesSave()) {
|
||||
return new ResponseEntity<Resource<?>>(HttpStatus.METHOD_NOT_ALLOWED);
|
||||
}
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
Function<ReferencedProperty, ResourceSupport> handler = new Function<ReferencedProperty, ResourceSupport>() {
|
||||
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
public ResourceSupport apply(ReferencedProperty prop) {
|
||||
|
||||
if (prop.property.isCollectionLike()) {
|
||||
|
||||
Collection<Object> coll = new ArrayList<Object>();
|
||||
|
||||
if (HttpMethod.POST.equals(repoRequest.getRequestMethod())) {
|
||||
coll.addAll((Collection<Object>) prop.propertyValue);
|
||||
}
|
||||
|
||||
for (Link l : incoming.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
|
||||
coll.add(propVal);
|
||||
}
|
||||
|
||||
prop.wrapper.setProperty(prop.property, coll);
|
||||
|
||||
} else if (prop.property.isMap()) {
|
||||
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
|
||||
if (HttpMethod.POST.equals(repoRequest.getRequestMethod())) {
|
||||
m.putAll((Map<String, Object>) prop.propertyValue);
|
||||
}
|
||||
|
||||
for (Link l : incoming.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
|
||||
m.put(l.getRel(), propVal);
|
||||
}
|
||||
|
||||
prop.wrapper.setProperty(prop.property, m);
|
||||
|
||||
} else {
|
||||
|
||||
if (HttpMethod.POST.equals(repoRequest.getRequestMethod())) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot POST a reference to this singular property since the property type is not a List or a Map.");
|
||||
}
|
||||
|
||||
if (incoming.getLinks().size() != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Must send only 1 link to update a property reference that isn't a List or a Map.");
|
||||
}
|
||||
|
||||
Object propVal = loadPropertyValue(prop.propertyType, incoming.getLinks().get(0).getHref());
|
||||
prop.wrapper.setProperty(prop.property, propVal);
|
||||
}
|
||||
@@ -330,6 +361,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
publisher.publishEvent(new BeforeLinkSaveEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
Object result = invoker.invokeSave(prop.wrapper.getBean());
|
||||
publisher.publishEvent(new AfterLinkSaveEvent(result, prop.propertyValue));
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -341,7 +373,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource<?>> deletePropertyReferenceId(final RepositoryRestRequest repoRequest,
|
||||
public ResponseEntity<ResourceSupport> deletePropertyReferenceId(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String id, @PathVariable String property, final @PathVariable String propertyId)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
@@ -351,12 +383,15 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
throw new NoSuchMethodError();
|
||||
}
|
||||
|
||||
Function<ReferencedProperty, Resource<?>> handler = new Function<ReferencedProperty, Resource<?>>() {
|
||||
Function<ReferencedProperty, ResourceSupport> handler = new Function<ReferencedProperty, ResourceSupport>() {
|
||||
|
||||
@Override
|
||||
public Resource<?> apply(ReferencedProperty prop) {
|
||||
public ResourceSupport apply(ReferencedProperty prop) {
|
||||
|
||||
if (null == prop.propertyValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (prop.property.isCollectionLike()) {
|
||||
Collection<Object> coll = new ArrayList<Object>();
|
||||
for (Object obj : (Collection<Object>) prop.propertyValue) {
|
||||
@@ -384,6 +419,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
publisher.publishEvent(new BeforeLinkDeleteEvent(prop.wrapper.getBean(), prop.propertyValue));
|
||||
Object result = invoker.invokeSave(prop.wrapper.getBean());
|
||||
publisher.publishEvent(new AfterLinkDeleteEvent(result, prop.propertyValue));
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -393,28 +429,21 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
return ControllerUtils.toResponseEntity(null, EMPTY_RESOURCE, HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
private Link propertyReferenceLink(Resource<?> resource, URI baseUri, String rel) {
|
||||
Link selfLink = resource.getLink("self");
|
||||
String objId = selfLink.getHref().substring(selfLink.getHref().lastIndexOf('/') + 1);
|
||||
return new Link(buildUri(baseUri, objId).toString(), rel);
|
||||
}
|
||||
|
||||
private Object loadPropertyValue(Class<?> type, String href) {
|
||||
String id = href.substring(href.lastIndexOf('/') + 1);
|
||||
return converter.convert(id, STRING_TYPE, TypeDescriptor.valueOf(type));
|
||||
}
|
||||
|
||||
private Resource<?> doWithReferencedProperty(RepositoryRestRequest repoRequest, String id, String propertyPath,
|
||||
Function<ReferencedProperty, Resource<?>> handler) throws NoSuchMethodException {
|
||||
private ResourceSupport doWithReferencedProperty(RepositoryRestRequest repoRequest, String id, String propertyPath,
|
||||
Function<ReferencedProperty, ResourceSupport> handler) throws NoSuchMethodException {
|
||||
|
||||
RepositoryInvoker repoMethodInvoker = repoRequest.getRepositoryInvoker();
|
||||
RepositoryInvoker invoker = repoRequest.getRepositoryInvoker();
|
||||
|
||||
if (!repoMethodInvoker.exposesFindOne()) {
|
||||
if (!invoker.exposesFindOne()) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
Object domainObj = converter.convert(id, STRING_TYPE,
|
||||
TypeDescriptor.valueOf(repoRequest.getPersistentEntity().getType()));
|
||||
Object domainObj = invoker.invokeFindOne(id);
|
||||
|
||||
if (null == domainObj) {
|
||||
throw new ResourceNotFoundException();
|
||||
@@ -444,13 +473,7 @@ public class RepositoryPropertyReferenceController extends AbstractRepositoryRes
|
||||
this.property = property;
|
||||
this.propertyValue = propertyValue;
|
||||
this.wrapper = wrapper;
|
||||
if (property.isCollectionLike()) {
|
||||
this.propertyType = property.getComponentType();
|
||||
} else if (property.isMap()) {
|
||||
this.propertyType = property.getMapValueType();
|
||||
} else {
|
||||
this.propertyType = property.getType();
|
||||
}
|
||||
this.propertyType = property.getActualType();
|
||||
this.entity = repositories.getPersistentEntity(propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.webmvc.support.JpaHelper;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.SearchResourceMappings;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
@@ -32,16 +31,14 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
class RepositoryRestRequest {
|
||||
|
||||
private final NativeWebRequest request;
|
||||
private final URI baseUri;
|
||||
private final ResourceMetadata resourceMetadata;
|
||||
private final RepositoryInvoker repoInvoker;
|
||||
private final PersistentEntity<?, ?> persistentEntity;
|
||||
|
||||
public RepositoryRestRequest(PersistentEntity<?, ?> entity, NativeWebRequest request, URI baseUri,
|
||||
ResourceMetadata repoInfo, RepositoryInvoker invoker) {
|
||||
public RepositoryRestRequest(PersistentEntity<?, ?> entity, NativeWebRequest request, ResourceMetadata repoInfo,
|
||||
RepositoryInvoker invoker) {
|
||||
|
||||
this.request = request;
|
||||
this.baseUri = baseUri;
|
||||
this.resourceMetadata = repoInfo;
|
||||
if (resourceMetadata == null || !resourceMetadata.isExported()) {
|
||||
|
||||
@@ -62,14 +59,18 @@ class RepositoryRestRequest {
|
||||
return HttpMethod.valueOf(request.getNativeRequest(HttpServletRequest.class).getMethod());
|
||||
}
|
||||
|
||||
URI getBaseUri() {
|
||||
return baseUri;
|
||||
Class<?> getDomainType() {
|
||||
return resourceMetadata.getDomainType();
|
||||
}
|
||||
|
||||
ResourceMetadata getResourceMetadata() {
|
||||
return resourceMetadata;
|
||||
}
|
||||
|
||||
SearchResourceMappings getSearchMappings() {
|
||||
return resourceMetadata.getSearchResourceMappings();
|
||||
}
|
||||
|
||||
RepositoryInvoker getRepositoryInvoker() {
|
||||
return repoInvoker;
|
||||
}
|
||||
|
||||
@@ -15,15 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvokerFactory;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvokerFactory;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
@@ -39,7 +37,6 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl
|
||||
private final Repositories repositories;
|
||||
private final RepositoryInvokerFactory invokerFactory;
|
||||
private final ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver;
|
||||
private final BaseUriMethodArgumentResolver baseUriResolver;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RepositoryRestRequestHandlerMethodArgumentResolver} using the given {@link Repositories} and
|
||||
@@ -49,8 +46,7 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl
|
||||
* @param conversionService must not be {@literal null}.
|
||||
*/
|
||||
public RepositoryRestRequestHandlerMethodArgumentResolver(Repositories repositories,
|
||||
ConversionService conversionService, ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver,
|
||||
BaseUriMethodArgumentResolver baseUriResolver) {
|
||||
ConversionService conversionService, ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver) {
|
||||
|
||||
Assert.notNull(repositories, "Repositories must not be null!");
|
||||
Assert.notNull(conversionService, "ConversionService must not be null!");
|
||||
@@ -58,7 +54,6 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl
|
||||
this.repositories = repositories;
|
||||
this.invokerFactory = new RepositoryInvokerFactory(repositories, conversionService);
|
||||
this.resourceMetadataResolver = resourceMetadataResolver;
|
||||
this.baseUriResolver = baseUriResolver;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -78,7 +73,6 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl
|
||||
public RepositoryRestRequest resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
URI baseUri = baseUriResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
|
||||
ResourceMetadata repoInfo = resourceMetadataResolver.resolveArgument(parameter, mavContainer, webRequest,
|
||||
binderFactory);
|
||||
|
||||
@@ -87,6 +81,6 @@ public class RepositoryRestRequestHandlerMethodArgumentResolver implements Handl
|
||||
|
||||
// TODO reject if ResourceMetadata cannot be resolved
|
||||
|
||||
return new RepositoryRestRequest(persistentEntity, webRequest, baseUri, repoInfo, repositoryInvoker);
|
||||
return new RepositoryRestRequest(persistentEntity, webRequest, repoInfo, repositoryInvoker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.data.rest.webmvc;
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -26,63 +25,201 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.repository.mapping.SearchResourceMappings;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvoker;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMapping;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.SearchResourceMappings;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Controller to lookup and execute searches on a given repository.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RestController
|
||||
class RepositorySearchController extends AbstractRepositoryRestController {
|
||||
|
||||
private static final String BASE_MAPPING = "/{repository}/search";
|
||||
private static final String SEARCH = "/search";
|
||||
private static final String BASE_MAPPING = "/{repository}" + SEARCH;
|
||||
|
||||
private final EntityLinks entityLinks;
|
||||
private final ResourceMappings mappings;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RepositorySearchController} using the given {@link PagedResourcesAssembler},
|
||||
* {@link PersistentEntityResourceAssembler}, {@link EntityLinks} and {@link ResourceMappings}.
|
||||
*
|
||||
* @param assembler must not be {@literal null}.
|
||||
* @param perAssembler must not be {@literal null}.
|
||||
* @param entityLinks must not be {@literal null}.
|
||||
* @param mappings must not be {@literal null}.
|
||||
*/
|
||||
@Autowired
|
||||
public RepositorySearchController(PagedResourcesAssembler<Object> assembler,
|
||||
PersistentEntityResourceAssembler<Object> perAssembler, EntityLinks entityLinks, ResourceMappings mappings) {
|
||||
|
||||
super(assembler, perAssembler);
|
||||
|
||||
Assert.notNull(entityLinks, "EntityLinks must not be null!");
|
||||
Assert.notNull(mappings, "ResourceMappings must not be null!");
|
||||
|
||||
this.entityLinks = entityLinks;
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-compact+json" })
|
||||
/**
|
||||
* Exposes links to the individual search resources exposed by the backing repository.
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
public Resource<?> list(RepositoryRestRequest repoRequest) throws ResourceNotFoundException {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
links.addAll(queryMethodLinks(repoRequest.getBaseUri(), repoRequest.getPersistentEntity().getType()));
|
||||
if (links.isEmpty()) {
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, //
|
||||
produces = { "application/json", "application/x-spring-data-compact+json" })
|
||||
public Resource<?> listSearches(RepositoryRestRequest request) {
|
||||
|
||||
SearchResourceMappings resourceMappings = request.getSearchMappings();
|
||||
|
||||
if (!resourceMappings.isExported()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
return new Resource<Object>(Collections.emptyList(), links);
|
||||
|
||||
Links queryMethodLinks = getSearchLinks(request.getDomainType());
|
||||
|
||||
if (queryMethodLinks.isEmpty()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
return new Resource<Object>(Collections.emptyList(), queryMethodLinks);
|
||||
}
|
||||
|
||||
protected List<Link> queryMethodLinks(URI baseUri, Class<?> domainType) {
|
||||
/**
|
||||
* Executes the search with the given name.
|
||||
*
|
||||
* @param request
|
||||
* @param repository
|
||||
* @param search
|
||||
* @param pageable
|
||||
* @return
|
||||
* @throws ResourceNotFoundException
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING + "/{search}", method = RequestMethod.GET, //
|
||||
produces = { "application/json", "application/x-spring-data-verbose+json" })
|
||||
public ResponseEntity<Resources<?>> executeSearch(RepositoryRestRequest request, @PathVariable String search,
|
||||
Pageable pageable) {
|
||||
|
||||
Method method = checkExecutability(request, search);
|
||||
Resources<?> resources = executeQueryMethod(request, method, pageable);
|
||||
|
||||
return new ResponseEntity<Resources<?>>(resources, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a query method and exposes the results in compact form.
|
||||
*
|
||||
* @param repoRequest
|
||||
* @param repository
|
||||
* @param method
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING + "/{method}", method = RequestMethod.GET, //
|
||||
produces = { "application/x-spring-data-compact+json" })
|
||||
public ResourceSupport executeSearchCompact(RepositoryRestRequest repoRequest, @PathVariable String repository,
|
||||
@PathVariable String search, Pageable pageable) {
|
||||
|
||||
Method method = checkExecutability(repoRequest, search);
|
||||
ResourceSupport resource = executeQueryMethod(repoRequest, method, pageable);
|
||||
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
LinkBuilder builder = entityLinks.linkFor(domainType).slash("search");
|
||||
|
||||
if (resource instanceof Resources && ((Resources<?>) resource).getContent() != null) {
|
||||
|
||||
for (Object obj : ((Resources<?>) resource).getContent()) {
|
||||
if (null != obj && obj instanceof Resource) {
|
||||
Resource<?> res = (Resource<?>) obj;
|
||||
links.add(resourceLink(repoRequest, res));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (resource instanceof Resource) {
|
||||
|
||||
Resource<?> res = (Resource<?>) resource;
|
||||
links.add(resourceLink(repoRequest, res));
|
||||
}
|
||||
|
||||
return new Resource<Object>(EMPTY_RESOURCE_LIST, links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the given request is actually executable. Will reject execution if we don't find a search with the
|
||||
* given name.
|
||||
*
|
||||
* @param request
|
||||
* @param searchName
|
||||
* @return
|
||||
*/
|
||||
private Method checkExecutability(RepositoryRestRequest request, String searchName) {
|
||||
|
||||
ResourceMetadata metadata = request.getResourceMetadata();
|
||||
SearchResourceMappings searchMapping = metadata.getSearchResourceMappings();
|
||||
|
||||
if (!searchMapping.isExported()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
Method method = searchMapping.getMappedMethod(searchName);
|
||||
|
||||
if (method == null) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repoRequest
|
||||
* @param method
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
private Resources<?> executeQueryMethod(final RepositoryRestRequest repoRequest, Method method, Pageable pageable) {
|
||||
|
||||
RepositoryInvoker repoMethodInvoker = repoRequest.getRepositoryInvoker();
|
||||
Map<String, String[]> parameters = repoRequest.getRequest().getParameterMap();
|
||||
Object result = repoMethodInvoker.invokeQueryMethod(method, parameters, pageable, null);
|
||||
|
||||
return resultToResources(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Links} to the individual searches exposed.
|
||||
*
|
||||
* @param domainType the domain type we want to obtain the search links for.
|
||||
* @return
|
||||
*/
|
||||
private Links getSearchLinks(Class<?> domainType) {
|
||||
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
LinkBuilder builder = entityLinks.linkFor(domainType).slash(SEARCH);
|
||||
|
||||
for (ResourceMapping mapping : mappings.getSearchResourceMappings(domainType)) {
|
||||
|
||||
@@ -90,62 +227,9 @@ class RepositorySearchController extends AbstractRepositoryRestController {
|
||||
continue;
|
||||
}
|
||||
|
||||
links.add(builder.slash(mapping.getPath().toString()).withRel(mapping.getRel()));
|
||||
links.add(builder.slash(mapping.getPath()).withRel(mapping.getRel()));
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING + "/{method}", method = RequestMethod.GET, produces = { "application/json",
|
||||
"application/x-spring-data-verbose+json" })
|
||||
@ResponseBody
|
||||
public ResponseEntity<ResourceSupport> query(final RepositoryRestRequest repoRequest,
|
||||
@PathVariable String repository, @PathVariable String method, Pageable pageable) throws ResourceNotFoundException {
|
||||
|
||||
ResourceMetadata metadata = repoRequest.getResourceMetadata();
|
||||
SearchResourceMappings searchMapping = metadata.getSearchResourceMappings();
|
||||
|
||||
if (searchMapping.isExported()) {
|
||||
return new ResponseEntity<ResourceSupport>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
RepositoryInvoker repoMethodInvoker = repoRequest.getRepositoryInvoker();
|
||||
|
||||
Method mappedMethod = searchMapping.getMappedMethod(method);
|
||||
|
||||
if (mappedMethod == null) {
|
||||
return new ResponseEntity<ResourceSupport>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
Map<String, String[]> parameters = repoRequest.getRequest().getParameterMap();
|
||||
Object result = repoMethodInvoker.invokeQueryMethod(mappedMethod, parameters, pageable, null);
|
||||
|
||||
return new ResponseEntity<ResourceSupport>(resultToResources(result), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING + "/{method}", method = RequestMethod.GET,
|
||||
produces = { "application/x-spring-data-compact+json" })
|
||||
public ResourceSupport queryCompact(RepositoryRestRequest repoRequest, @PathVariable String repository,
|
||||
@PathVariable String method, Pageable pageable) {
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
ResponseEntity<ResourceSupport> entity = query(repoRequest, repository, method, pageable);
|
||||
ResourceSupport resource = entity.getBody();
|
||||
links.addAll(resource.getLinks());
|
||||
|
||||
if (resource instanceof Resources && ((Resources<?>) resource).getContent() != null) {
|
||||
for (Object obj : ((Resources<?>) resource).getContent()) {
|
||||
if (null != obj && obj instanceof Resource) {
|
||||
Resource<?> res = (Resource<?>) obj;
|
||||
links.add(resourceLink(repoRequest, res));
|
||||
}
|
||||
}
|
||||
} else if (resource instanceof Resource) {
|
||||
Resource<?> res = (Resource<?>) resource;
|
||||
links.add(resourceLink(repoRequest, res));
|
||||
}
|
||||
|
||||
return new Resource<Object>(EMPTY_RESOURCE_LIST, links);
|
||||
return new Links(links);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ 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.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* Indicates a resource was not found.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public class ResourceNotFoundException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 7992904489502842099L;
|
||||
|
||||
public ResourceNotFoundException() {
|
||||
super("Resource not found");
|
||||
super("Resource not found!");
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(String message) {
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.util.ClassTypeInformation.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
@@ -33,14 +41,6 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.util.ClassTypeInformation.from;
|
||||
|
||||
/**
|
||||
* {@link HandlerMethodReturnValueHandler} to post-process the objects returned from controller methods using the
|
||||
* configured {@link ResourceProcessor}s.
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.springframework.data.rest.webmvc.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marker annotation to denote which {@link java.net.URI} parameter should be resolved to the request base URI.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@Target({ ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BaseURI {
|
||||
}
|
||||
@@ -32,15 +32,14 @@ import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.convert.ISO8601DateConverter;
|
||||
import org.springframework.data.rest.convert.UUIDConverter;
|
||||
import org.springframework.data.rest.repository.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.repository.context.AnnotatedHandlerBeanPostProcessor;
|
||||
import org.springframework.data.rest.repository.context.ValidatingRepositoryEventListener;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.support.DomainObjectMerger;
|
||||
import org.springframework.data.rest.webmvc.BaseUriMethodArgumentResolver;
|
||||
import org.springframework.data.rest.core.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.core.event.AnnotatedHandlerBeanPostProcessor;
|
||||
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvokerFactory;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.support.DomainObjectMerger;
|
||||
import org.springframework.data.rest.core.util.UUIDConverter;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResourceHandlerMethodArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter;
|
||||
@@ -107,7 +106,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
|
||||
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
conversionService.addConverter(UUIDConverter.INSTANCE);
|
||||
conversionService.addConverter(ISO8601DateConverter.INSTANCE);
|
||||
configureConversionService(conversionService);
|
||||
return conversionService;
|
||||
}
|
||||
@@ -185,16 +183,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
return new DomainObjectMerger(repositories(), defaultConversionService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the base {@link java.net.URI} under which this application is configured.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public BaseUriMethodArgumentResolver baseUriMethodArgumentResolver() {
|
||||
return new BaseUriMethodArgumentResolver(config());
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns an {@link javax.servlet.http.HttpServletRequest} into a
|
||||
* {@link org.springframework.http.server.ServerHttpRequest}.
|
||||
@@ -214,7 +202,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
@Bean
|
||||
public RepositoryRestRequestHandlerMethodArgumentResolver repoRequestArgumentResolver() {
|
||||
return new RepositoryRestRequestHandlerMethodArgumentResolver(repositories(), defaultConversionService(),
|
||||
resourceMetadataHandlerMethodArgumentResolver(), baseUriMethodArgumentResolver());
|
||||
resourceMetadataHandlerMethodArgumentResolver());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -378,6 +366,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
return er;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RepositoryInvokerFactory repositoryInvokerFactory() {
|
||||
return new RepositoryInvokerFactory(repositories(), defaultConversionService());
|
||||
}
|
||||
|
||||
private List<HttpMessageConverter<?>> defaultMessageConverters() {
|
||||
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
messageConverters.add(jacksonHttpMessageConverter());
|
||||
@@ -386,8 +379,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
}
|
||||
|
||||
private List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers() {
|
||||
return Arrays.asList(baseUriMethodArgumentResolver(), pageableResolver(), sortResolver(),
|
||||
serverHttpRequestMethodArgumentResolver(), repoRequestArgumentResolver(), persistentEntityArgumentResolver(),
|
||||
return Arrays.asList(pageableResolver(), sortResolver(), serverHttpRequestMethodArgumentResolver(),
|
||||
repoRequestArgumentResolver(), persistentEntityArgumentResolver(),
|
||||
resourceMetadataHandlerMethodArgumentResolver());
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.repository.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.UriDomainClassConverter;
|
||||
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.PersistentEntityResource;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -20,9 +19,9 @@ import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.repository.annotation.Description;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.annotation.Description;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder;
|
||||
import org.springframework.hateoas.Link;
|
||||
|
||||
@@ -90,9 +89,7 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric
|
||||
public void doWithPersistentProperty(PersistentProperty persistentProperty) {
|
||||
Class<?> propertyType = persistentProperty.getType();
|
||||
String type = uncapitalize(propertyType.getSimpleName());
|
||||
boolean notNull = persistentProperty.getField().isAnnotationPresent(Nonnull.class)
|
||||
|| persistentProperty.getGetter().isAnnotationPresent(Nonnull.class)
|
||||
|| persistentProperty.getField().isAnnotationPresent(NotNull.class)
|
||||
boolean notNull = persistentProperty.getField().isAnnotationPresent(NotNull.class)
|
||||
|| persistentProperty.getGetter().isAnnotationPresent(NotNull.class);
|
||||
String desc = persistentProperty.getField().isAnnotationPresent(Description.class) ? persistentProperty
|
||||
.getField().getAnnotation(Description.class).value() : persistentProperty.getGetter().isAnnotationPresent(
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.springframework.data.rest.webmvc.support;
|
||||
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResource;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceProcessor;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class PersistentEntityResourceProcessor implements ResourceProcessor<PersistentEntityResource<?>> {
|
||||
|
||||
private final List<DomainTypeResourceProcessor> resourceProcessors = new ArrayList<DomainTypeResourceProcessor>();
|
||||
|
||||
@Autowired
|
||||
public PersistentEntityResourceProcessor(Repositories repositories,
|
||||
List<ResourceProcessor<Resource<?>>> resourceProcessors) {
|
||||
if (null != resourceProcessors) {
|
||||
for (ResourceProcessor<Resource<?>> rp : resourceProcessors) {
|
||||
TypeInformation<?> typeInfo = ClassTypeInformation.from(rp.getClass());
|
||||
TypeInformation<?> domainType = typeInfo.getTypeArguments().get(0);
|
||||
if (null != repositories.getPersistentEntity(domainType.getType())) {
|
||||
this.resourceProcessors.add(new DomainTypeResourceProcessor(domainType.getType(), rp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentEntityResource<?> process(PersistentEntityResource<?> resource) {
|
||||
Object content = resource.getContent();
|
||||
if (null == content) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
Class<?> domainType = content.getClass();
|
||||
for (DomainTypeResourceProcessor rp : resourceProcessors) {
|
||||
if (isAssignable(domainType, rp.domainType)) {
|
||||
rp.resourceProcessor.process(resource);
|
||||
}
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
private static class DomainTypeResourceProcessor {
|
||||
final Class<?> domainType;
|
||||
final ResourceProcessor<Resource<?>> resourceProcessor;
|
||||
|
||||
private DomainTypeResourceProcessor(Class<?> domainType, ResourceProcessor<Resource<?>> resourceProcessor) {
|
||||
this.domainType = domainType;
|
||||
this.resourceProcessor = resourceProcessor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,11 +4,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.data.rest.repository.RepositoryConstraintViolationException;
|
||||
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
|
||||
import org.springframework.validation.FieldError;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,9 @@ package org.springframework.data.rest.webmvc.support;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
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.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.core.AbstractEntityLinks;
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.data.rest.webmvc.support;
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.RepositoryController;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.core.LinkBuilderSupport;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.core.Path;
|
||||
import org.springframework.data.rest.core.invoke.RepositoryInvokerFactory;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
|
||||
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* Base class to write integration tests for controllers.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = { RepositoryRestMvcConfiguration.class, JpaRepositoryConfig.class })
|
||||
public class AbstractControllerIntegrationTests {
|
||||
|
||||
public static final Path BASE = new Path("http://localhost");
|
||||
|
||||
@Autowired Repositories repositories;
|
||||
@Autowired RepositoryInvokerFactory invokerFactory;
|
||||
@Autowired ResourceMappings mappings;
|
||||
|
||||
/**
|
||||
* Returns a {@link RepositoryRestRequest} for the given domain type.
|
||||
*
|
||||
* @param domainType must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected RepositoryRestRequest getRequest(Class<?> domainType) {
|
||||
return getRequest(domainType, RequestParameters.NONE);
|
||||
}
|
||||
|
||||
protected RepositoryRestRequest getRequest(Class<?> domainType, RequestParameters parameters) {
|
||||
|
||||
Assert.notNull(domainType, "Domain type must not be null!");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setParameters(parameters.asMap());
|
||||
|
||||
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
|
||||
RequestContextHolder.setRequestAttributes(requestAttributes);
|
||||
|
||||
PersistentEntity<?, ?> entity = repositories.getPersistentEntity(domainType);
|
||||
|
||||
return new RepositoryRestRequest(entity, new ServletWebRequest(request), mappings.getMappingFor(domainType),
|
||||
invokerFactory.getInvokerFor(domainType));
|
||||
}
|
||||
|
||||
protected ResourceMetadata getMetadata(Class<?> domainType) {
|
||||
return mappings.getMappingFor(domainType);
|
||||
}
|
||||
}
|
||||
@@ -1,101 +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.webmvc;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.webmvc.annotation.BaseURI;
|
||||
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CustomMethodArgumentResolverTests {
|
||||
|
||||
static final MethodParameter BASE_URI;
|
||||
static final MethodParameter PAGE_SORT;
|
||||
|
||||
static {
|
||||
try {
|
||||
BASE_URI = MethodParameter.forMethodOrConstructor(Methods.class.getDeclaredMethod("baseUri", URI.class), 0);
|
||||
PAGE_SORT = MethodParameter.forMethodOrConstructor(
|
||||
Methods.class.getDeclaredMethod("pagingAndSorting", Pageable.class), 0);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final RepositoryRestConfiguration config = new RepositoryRestConfiguration().setBaseUri(URI
|
||||
.create("http://localhost:8080"));
|
||||
private final BaseUriMethodArgumentResolver baseUriResolver = new BaseUriMethodArgumentResolver(config);
|
||||
private final PageableHandlerMethodArgumentResolver pageSortResolver = new PageableHandlerMethodArgumentResolver();
|
||||
private ModelAndViewContainer mavContainer;
|
||||
@Mock WebDataBinderFactory webDataBinderFactory;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
pageSortResolver.setOneIndexedParameters(true);
|
||||
pageSortResolver.setFallbackPageable(new PageRequest(1, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void baseUriMethodArgumentResolver() throws Exception {
|
||||
assertThat("Finds @BaseURI-annotated java.net.URI parameter", baseUriResolver.supportsParameter(BASE_URI), is(true));
|
||||
|
||||
// Resolve the base URI
|
||||
URI baseUri = (URI) baseUriResolver.resolveArgument(BASE_URI, mavContainer, new ServletWebRequest(
|
||||
Requests.ROOT_REQUEST), webDataBinderFactory);
|
||||
|
||||
assertThat("Base URI should be 'http://localhost:8080'", baseUri.toString(), is("http://localhost:8080"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pagingAndSortingMethodArgumentResolver() throws Exception {
|
||||
assertThat("Finds PagingAndSorting parameter", pageSortResolver.supportsParameter(PAGE_SORT), is(true));
|
||||
|
||||
// Resolve Page and Sort information
|
||||
Pageable pageSort = pageSortResolver.resolveArgument(PAGE_SORT, mavContainer, new ServletWebRequest(
|
||||
Requests.PAGE_REQUEST), webDataBinderFactory);
|
||||
|
||||
assertThat("Finds page parameter value", pageSort.getPageNumber(), is(1));
|
||||
assertThat("Finds limit parameter value", pageSort.getPageSize(), is(10));
|
||||
}
|
||||
|
||||
static class Methods {
|
||||
void baseUri(@BaseURI URI baseUri) {}
|
||||
|
||||
void pagingAndSorting(Pageable pageSort) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 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.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.webmvc.ResourceTester.HasSelfLink;
|
||||
import org.springframework.data.rest.webmvc.jpa.CreditCard;
|
||||
import org.springframework.data.rest.webmvc.jpa.Order;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
import org.springframework.hateoas.PagedResources;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* Integration tests for the {@link RepositorySearchController}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RepositorySearchControllerIntegrationTests extends AbstractControllerIntegrationTests {
|
||||
|
||||
@Autowired RepositorySearchController controller;
|
||||
|
||||
@Test
|
||||
public void rendersCorrectSearchLinksForPersons() {
|
||||
|
||||
RepositoryRestRequest request = getRequest(Person.class);
|
||||
Resource<?> resource = controller.listSearches(request);
|
||||
|
||||
ResourceTester tester = ResourceTester.of(resource);
|
||||
tester.assertNumberOfLinks(4);
|
||||
tester.assertHasLink("findFirstPersonByFirstName", "http://localhost/people/search/findFirstPersonByFirstName");
|
||||
tester.assertHasLink("firstname", "http://localhost/people/search/firstname");
|
||||
tester.assertHasLink("findByCreatedUsingISO8601Date",
|
||||
"http://localhost/people/search/findByCreatedUsingISO8601Date");
|
||||
tester.assertHasLink("findByCreatedGreaterThan", "http://localhost/people/search/findByCreatedGreaterThan");
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void returns404ForUnexportedRepository() {
|
||||
|
||||
RepositoryRestRequest request = getRequest(CreditCard.class);
|
||||
controller.listSearches(request);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void returns404ForRepositoryWithoutSearches() {
|
||||
|
||||
RepositoryRestRequest request = getRequest(Order.class);
|
||||
controller.listSearches(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executesSearchAgainstRepository() {
|
||||
|
||||
RequestParameters parameters = new RequestParameters("firstName", "John");
|
||||
RepositoryRestRequest request = getRequest(Person.class, parameters);
|
||||
|
||||
ResponseEntity<Resources<?>> response = controller.executeSearch(request, "firstname", null);
|
||||
|
||||
ResourceTester tester = ResourceTester.of(response.getBody());
|
||||
PagedResources<Object> pagedResources = tester.assertIsPage();
|
||||
assertThat(pagedResources.getContent().size(), is(1));
|
||||
|
||||
ResourceMetadata metadata = getMetadata(Person.class);
|
||||
tester.withContentResource(new HasSelfLink(BASE.slash(metadata.getPath()).slash("{id}")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class RequestParameters {
|
||||
|
||||
public static RequestParameters NONE = new RequestParameters();
|
||||
|
||||
private final Map<String, String[]> parameters;
|
||||
|
||||
public RequestParameters(String key, String... values) {
|
||||
this(new HashMap<String, String[]>(), key, values);
|
||||
}
|
||||
|
||||
private RequestParameters(Map<String, String[]> parameters, String key, String... values) {
|
||||
|
||||
Assert.notNull(parameters, "Parameters must not be null!");
|
||||
Assert.hasText(key, "Key must not be null or empty!");
|
||||
|
||||
this.parameters = new HashMap<String, String[]>(parameters);
|
||||
this.parameters.put(key, values);
|
||||
}
|
||||
|
||||
private RequestParameters() {
|
||||
this.parameters = new HashMap<String, String[]>();
|
||||
}
|
||||
|
||||
public RequestParameters and(String key, String... values) {
|
||||
return new RequestParameters(parameters, key, values);
|
||||
}
|
||||
|
||||
public Map<String, String[]> asMap() {
|
||||
return Collections.unmodifiableMap(parameters);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.rest.webmvc.HttpEntityMatcher.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.hateoas.mvc.HeaderLinksResponseEntity;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.springframework.data.rest.core.Path;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.PagedResources;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
/**
|
||||
* Simple wrapper for {@link Resource}s to allow easy assertions on it.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ResourceTester {
|
||||
|
||||
private final ResourceSupport resource;
|
||||
|
||||
public static ResourceTester of(Object object) {
|
||||
assertThat(object, is(instanceOf(ResourceSupport.class)));
|
||||
return new ResourceTester((ResourceSupport) object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceTester} for the given {@link ResourceSupport}.
|
||||
*
|
||||
* @param resource must not be {@literal null}.
|
||||
*/
|
||||
private ResourceTester(ResourceSupport resource) {
|
||||
Assert.notNull(resource, "Resource must not be null!");
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the {@link Resource} contains the given number of {@link Link}s.
|
||||
*
|
||||
* @param number
|
||||
*/
|
||||
public void assertNumberOfLinks(int number) {
|
||||
assertThat(resource.getLinks().size(), is(number));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the {@link Resource} has a linke with the given rel and href.
|
||||
*
|
||||
* @param rel must not be {@literal null}.
|
||||
* @param href can be {@literal null}, if so, only the presence of a {@link Link} with the given rel is checked.
|
||||
*/
|
||||
public Link assertHasLink(String rel, String href) {
|
||||
|
||||
Link link = resource.getLink(rel);
|
||||
assertThat("Expected link with rel '" + rel + "' but didn't find it in " + resource.getLinks(), link,
|
||||
is(notNullValue()));
|
||||
|
||||
if (href != null) {
|
||||
assertThat(link.getHref(), is(href));
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> PagedResources<T> assertIsPage() {
|
||||
|
||||
assertThat(resource, is(instanceOf(PagedResources.class)));
|
||||
return (PagedResources<T>) resource;
|
||||
}
|
||||
|
||||
public ResourceTester getContentResource() {
|
||||
|
||||
assertThat(resource, is(instanceOf(Resources.class)));
|
||||
Object next = ((Resources<?>) resource).getContent().iterator().next();
|
||||
|
||||
assertThat(next, is(instanceOf(ResourceSupport.class)));
|
||||
return new ResourceTester((ResourceSupport) next);
|
||||
}
|
||||
|
||||
public void withContentResource(ContentResourceHandler handler) {
|
||||
|
||||
assertThat(resource, is(instanceOf(Resources.class)));
|
||||
|
||||
for (Object element : ((Resources<?>) resource).getContent()) {
|
||||
assertThat(element, is(instanceOf(ResourceSupport.class)));
|
||||
handler.doWith(of(element));
|
||||
}
|
||||
}
|
||||
|
||||
public interface ContentResourceHandler {
|
||||
|
||||
void doWith(ResourceTester content);
|
||||
}
|
||||
|
||||
public static class HasSelfLink implements ContentResourceHandler {
|
||||
|
||||
private final Path template;
|
||||
|
||||
public HasSelfLink(Path template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.webmvc.ResourceTester.ContentResourceHandler#doWith(org.springframework.data.rest.webmvc.ResourceTester)
|
||||
*/
|
||||
@Override
|
||||
public void doWith(ResourceTester content) {
|
||||
|
||||
String href = content.assertHasLink("self", null).getHref();
|
||||
|
||||
UriTemplate uriTemplate = new UriTemplate(template.toString());
|
||||
assertThat(String.format("Expected %s to match %s!", href, uriTemplate.toString()), uriTemplate.matches(href),
|
||||
is(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,15 @@ package org.springframework.data.rest.webmvc.gemfire;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
|
||||
/**
|
||||
* Value object to represent email addresses.
|
||||
*
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.webmvc.AbstractWebIntegrationTests;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Entity
|
||||
public class Order {
|
||||
|
||||
@Id private Long id;
|
||||
@ManyToOne private Person creator;
|
||||
|
||||
public Order(Person creator) {
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
protected Order() {
|
||||
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Person getCreator() {
|
||||
return creator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface OrderRepository extends CrudRepository<Order, Long> {
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
@@ -13,7 +14,7 @@ import javax.persistence.ManyToOne;
|
||||
import javax.persistence.PrePersist;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.data.rest.repository.annotation.Description;
|
||||
import org.springframework.data.rest.core.annotation.Description;
|
||||
|
||||
/**
|
||||
* An entity that represents a person.
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -7,9 +22,9 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.convert.ISO8601DateConverter;
|
||||
import org.springframework.data.rest.repository.annotation.ConvertWith;
|
||||
import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
|
||||
/**
|
||||
* A repository to manage {@link Person}s.
|
||||
@@ -20,14 +35,13 @@ import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
|
||||
|
||||
@RestResource(rel = "firstname", path = "firstname")
|
||||
public Page<Person> findByFirstName(@Param("firstName") String firstName, Pageable pageable);
|
||||
Page<Person> findByFirstName(@Param("firstName") String firstName, Pageable pageable);
|
||||
|
||||
public Person findFirstPersonByFirstName(@Param("firstName") String firstName);
|
||||
Person findFirstPersonByFirstName(@Param("firstName") String firstName);
|
||||
|
||||
public Page<Person> findByCreatedGreaterThan(@Param("date") Date date, Pageable pageable);
|
||||
Page<Person> findByCreatedGreaterThan(@Param("date") Date date, Pageable pageable);
|
||||
|
||||
@Query("select p from Person p where p.created > :date")
|
||||
public Page<Person> findByCreatedUsingISO8601Date(@Param("date") @ConvertWith(ISO8601DateConverter.class) Date date,
|
||||
Page<Person> findByCreatedUsingISO8601Date(@Param("date") @DateTimeFormat(iso = ISO.DATE_TIME) Date date,
|
||||
Pageable pageable);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.repository.PersistentEntityResource;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResource;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
|
||||
import org.springframework.hateoas.Link;
|
||||
|
||||
@@ -9,9 +9,9 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.repository.support.DomainClassConverter;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.repository.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.UriDomainClassConverter;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.rest.webmvc.mongodb;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -26,6 +25,8 @@ import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
|
||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user