Tweaking query method support.
This commit is contained in:
@@ -14,7 +14,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.servlet.view.AbstractView;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class JsonView extends AbstractView {
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.springframework.data.rest.core.Link;
|
||||
import org.springframework.data.rest.core.SimpleLink;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class Links {
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ import java.util.List;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.rest.repository.context.ValidatingRepositoryEventListener;
|
||||
import org.springframework.data.rest.repository.jpa.JpaRepositoryExporter;
|
||||
@@ -19,10 +20,11 @@ import org.springframework.http.converter.json.MappingJacksonHttpMessageConverte
|
||||
import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* Base configuration for the Spring Data REST Exporter.
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Configuration
|
||||
@ImportResource("classpath*:META-INF/spring-data-rest/**/*-export.xml")
|
||||
public class RepositoryRestConfiguration {
|
||||
|
||||
@Autowired
|
||||
@@ -31,12 +33,17 @@ public class RepositoryRestConfiguration {
|
||||
JpaRepositoryExporter jpaRepositoryExporter;
|
||||
@Autowired(required = false)
|
||||
ConversionService customConversionService;
|
||||
ConversionService defaultConversionService = new DefaultConversionService();
|
||||
ConfigurableConversionService defaultConversionService = new DefaultConversionService();
|
||||
@Autowired(required = false)
|
||||
List<HttpMessageConverter<?>> httpMessageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
@Autowired(required = false)
|
||||
ValidatingRepositoryEventListener validatingRepositoryEventListener;
|
||||
|
||||
/**
|
||||
* Either the user's pre-configured {@link ConversionService} or the {@link DefaultConversionService}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean ConversionService conversionService() {
|
||||
if (null != customConversionService) {
|
||||
return customConversionService;
|
||||
@@ -45,6 +52,11 @@ public class RepositoryRestConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of {@link HttpMessageConverter}s to be used to read incoming data and to write outgoing responses.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean List<HttpMessageConverter<?>> httpMessageConverters() {
|
||||
if (httpMessageConverters.isEmpty()) {
|
||||
MappingJacksonHttpMessageConverter json = new MappingJacksonHttpMessageConverter();
|
||||
@@ -56,6 +68,11 @@ public class RepositoryRestConfiguration {
|
||||
return httpMessageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export any JPA {@link org.springframework.data.repository.Repository} implementations we find.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean JpaRepositoryExporter jpaRepositoryExporter() {
|
||||
if (null == jpaRepositoryExporter) {
|
||||
jpaRepositoryExporter = new JpaRepositoryExporter();
|
||||
|
||||
@@ -26,6 +26,8 @@ import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.rest.core.Handler;
|
||||
@@ -39,6 +41,7 @@ import org.springframework.data.rest.repository.RepositoryExporter;
|
||||
import org.springframework.data.rest.repository.RepositoryExporterSupport;
|
||||
import org.springframework.data.rest.repository.RepositoryMetadata;
|
||||
import org.springframework.data.rest.repository.RepositoryQueryMethod;
|
||||
import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
import org.springframework.data.rest.repository.context.AfterDeleteEvent;
|
||||
import org.springframework.data.rest.repository.context.AfterLinkSaveEvent;
|
||||
import org.springframework.data.rest.repository.context.AfterSaveEvent;
|
||||
@@ -55,10 +58,12 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@@ -69,7 +74,7 @@ import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Controller
|
||||
public class RepositoryRestController
|
||||
@@ -90,6 +95,7 @@ public class RepositoryRestController
|
||||
private MediaType jsonMediaType = MediaType.parseMediaType("application/x-spring-data+json");
|
||||
private ConversionService conversionService = new DefaultConversionService();
|
||||
private List<HttpMessageConverter<?>> httpMessageConverters = Collections.emptyList();
|
||||
private Map<String, Handler<Object, Object>> resourceHandlers = Collections.emptyMap();
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
@@ -130,6 +136,24 @@ public class RepositoryRestController
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Handler<Object, Object>> getResourceHandlers() {
|
||||
return resourceHandlers;
|
||||
}
|
||||
|
||||
public RepositoryRestController setResourceHandlers(Map<String, Handler<Object, Object>> resourceHandlers) {
|
||||
this.resourceHandlers = resourceHandlers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Handler<Object, Object>> resourceHandlers() {
|
||||
return resourceHandlers;
|
||||
}
|
||||
|
||||
public RepositoryRestController resourceHandlers(Map<String, Handler<Object, Object>> resourceHandlers) {
|
||||
this.resourceHandlers = resourceHandlers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MediaType getUriListMediaType() {
|
||||
return uriListMediaType;
|
||||
}
|
||||
@@ -199,9 +223,12 @@ public class RepositoryRestController
|
||||
URI baseUri = uriBuilder.build().toUri();
|
||||
|
||||
Links links = new Links();
|
||||
for (RepositoryExporter repoMeta : repositoryExporters) {
|
||||
for (String name : (Set<String>) repoMeta.repositoryNames()) {
|
||||
links.add(new SimpleLink(name, buildUri(baseUri, name)));
|
||||
for (RepositoryExporter repoExporter : repositoryExporters) {
|
||||
for (String name : (Set<String>) repoExporter.repositoryNames()) {
|
||||
RepositoryMetadata repoMeta = repoExporter.repositoryMetadataFor(name);
|
||||
String rel = repoMeta.rel();
|
||||
URI path = buildUri(baseUri, name);
|
||||
links.add(new SimpleLink(rel, path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,13 +256,44 @@ public class RepositoryRestController
|
||||
while (iter.hasNext()) {
|
||||
Object o = iter.next();
|
||||
Serializable id = (Serializable) repoMeta.entityMetadata().idAttribute().get(o);
|
||||
links.add(new SimpleLink(repository + "." + o.getClass().getSimpleName(),
|
||||
links.add(new SimpleLink(repoMeta.rel() + "." + o.getClass().getSimpleName(),
|
||||
buildUri(baseUri, repository, id.toString())));
|
||||
}
|
||||
links.add(new SimpleLink(repoMeta.rel() + ".search",
|
||||
buildUri(baseUri, repository, "search")));
|
||||
|
||||
model.addAttribute(STATUS, HttpStatus.OK);
|
||||
model.addAttribute(RESOURCE, links);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@RequestMapping(
|
||||
value = "/{repository}/search",
|
||||
method = RequestMethod.GET,
|
||||
produces = {
|
||||
"application/json"
|
||||
}
|
||||
)
|
||||
public void listQueryMethods(UriComponentsBuilder uriBuilder,
|
||||
@PathVariable String repository,
|
||||
Model model) {
|
||||
URI baseUri = uriBuilder.build().toUri();
|
||||
|
||||
RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
|
||||
Links links = new Links();
|
||||
|
||||
for (Map.Entry<String, RepositoryQueryMethod> entry : ((Map<String, RepositoryQueryMethod>) repoMeta.queryMethods())
|
||||
.entrySet()) {
|
||||
links.add(new SimpleLink(repository + "." + entry.getKey(),
|
||||
buildUri(baseUri, repository, "search", entry.getKey())));
|
||||
String rel = repoMeta.rel() + "." + entry.getKey();
|
||||
URI path = buildUri(baseUri, repository, "search", entry.getKey());
|
||||
RestResource resourceAnno = entry.getValue().method().getAnnotation(RestResource.class);
|
||||
if (null != resourceAnno) {
|
||||
path = buildUri(baseUri, repository, "search", resourceAnno.path());
|
||||
if (StringUtils.hasText(resourceAnno.rel())) {
|
||||
rel = repoMeta.rel() + "." + resourceAnno.rel();
|
||||
}
|
||||
}
|
||||
links.add(new SimpleLink(rel, path));
|
||||
}
|
||||
|
||||
model.addAttribute(STATUS, HttpStatus.OK);
|
||||
@@ -266,10 +324,23 @@ public class RepositoryRestController
|
||||
Object[] paramVals = new Object[paramTypes.length];
|
||||
for (int i = 0; i < paramVals.length; i++) {
|
||||
String queryVal = request.getParameter(paramNames[i]);
|
||||
if (paramTypes[i].isAssignableFrom(String.class)) {
|
||||
if (String.class.isAssignableFrom(paramTypes[i])) {
|
||||
// Param type is a String
|
||||
paramVals[i] = queryVal;
|
||||
} else {
|
||||
} else if (Pageable.class.isAssignableFrom(paramTypes[i])) {
|
||||
// Handle paging
|
||||
} else if (Sort.class.isAssignableFrom(paramTypes[i])) {
|
||||
// Handle sorting
|
||||
} else if (conversionService.canConvert(String.class, paramTypes[i])) {
|
||||
// There's a converter from String -> param type
|
||||
paramVals[i] = conversionService.convert(queryVal, paramTypes[i]);
|
||||
} else {
|
||||
// Param type isn't a "simple" type or no converter exists, try JSON
|
||||
try {
|
||||
paramVals[i] = objectMapper.readValue(queryVal, paramTypes[i]);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,8 +351,10 @@ public class RepositoryRestController
|
||||
for (Object o : (Collection) result) {
|
||||
RepositoryMetadata elemRepoMeta = repositoryMetadataFor(o.getClass());
|
||||
if (null != elemRepoMeta) {
|
||||
Map<String, Object> dto = extractPropertiesLinkAware(repository, o, elemRepoMeta.entityMetadata(), baseUri);
|
||||
coll.add(dto);
|
||||
String id = elemRepoMeta.entityMetadata().idAttribute().get(o).toString();
|
||||
String rel = elemRepoMeta.rel() + "." + elemRepoMeta.entityMetadata().type().getSimpleName();
|
||||
URI path = buildUri(baseUri, repository, id);
|
||||
coll.add(new SimpleLink(rel, path));
|
||||
} else {
|
||||
coll.add(o);
|
||||
}
|
||||
@@ -292,11 +365,11 @@ public class RepositoryRestController
|
||||
} else {
|
||||
RepositoryMetadata elemRepoMeta = repositoryMetadataFor(result.getClass());
|
||||
if (null != elemRepoMeta) {
|
||||
Map<String, Object> dto = extractPropertiesLinkAware(repository,
|
||||
result,
|
||||
elemRepoMeta.entityMetadata(),
|
||||
baseUri);
|
||||
model.addAttribute(RESOURCE, dto);
|
||||
String id = elemRepoMeta.entityMetadata().idAttribute().get(result).toString();
|
||||
String rel = elemRepoMeta.rel() + "." + elemRepoMeta.entityMetadata().type().getSimpleName();
|
||||
URI path = buildUri(baseUri, repository, id);
|
||||
Link link = new SimpleLink(rel, path);
|
||||
model.addAttribute(RESOURCE, link);
|
||||
} else {
|
||||
model.addAttribute(RESOURCE, result);
|
||||
}
|
||||
@@ -387,12 +460,10 @@ public class RepositoryRestController
|
||||
headers.set("ETag", "\"" + version.toString() + "\"");
|
||||
}
|
||||
Map<String, Object> entityDto = extractPropertiesLinkAware(repository,
|
||||
repoMeta.rel(),
|
||||
entity,
|
||||
repoMeta.entityMetadata(),
|
||||
UriComponentsBuilder.fromUri(baseUri)
|
||||
.pathSegment(repository, id)
|
||||
.build()
|
||||
.toUri());
|
||||
buildUri(baseUri, repository, id));
|
||||
addSelfLink(baseUri, entityDto, repository, id);
|
||||
|
||||
model.addAttribute(HEADERS, headers);
|
||||
@@ -549,14 +620,14 @@ public class RepositoryRestController
|
||||
if (propVal instanceof Collection) {
|
||||
for (Object o : (Collection) propVal) {
|
||||
String propValId = idAttr.get(o).toString();
|
||||
URI uri = buildUri(baseUri, repository, id, property, propValId);
|
||||
links.add(new SimpleLink(repository + "." + entity.getClass()
|
||||
.getSimpleName() + "." + attrType.getSimpleName(), uri));
|
||||
String rel = repository + "." + entity.getClass().getSimpleName() + "." + attrType.getSimpleName();
|
||||
URI path = buildUri(baseUri, repository, id, property, propValId);
|
||||
links.add(new SimpleLink(rel, path));
|
||||
}
|
||||
} else if (propVal instanceof Map) {
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) propVal).entrySet()) {
|
||||
String propValId = idAttr.get(entry.getValue()).toString();
|
||||
URI uri = buildUri(baseUri, repository, id, property, propValId);
|
||||
URI path = buildUri(baseUri, repository, id, property, propValId);
|
||||
Object oKey = entry.getKey();
|
||||
String sKey;
|
||||
if (ClassUtils.isAssignable(oKey.getClass(), String.class)) {
|
||||
@@ -564,14 +635,14 @@ public class RepositoryRestController
|
||||
} else {
|
||||
sKey = conversionService.convert(oKey, String.class);
|
||||
}
|
||||
links.add(new SimpleLink(repository + "." + entity.getClass()
|
||||
.getSimpleName() + "." + sKey, uri));
|
||||
String rel = repository + "." + entity.getClass().getSimpleName() + "." + sKey;
|
||||
links.add(new SimpleLink(rel, path));
|
||||
}
|
||||
} else {
|
||||
String propValId = idAttr.get(propVal).toString();
|
||||
URI uri = buildUri(baseUri, repository, id, property, propValId);
|
||||
links.add(new SimpleLink(repository + "." + entity.getClass()
|
||||
.getSimpleName() + "." + property, uri));
|
||||
String rel = repository + "." + entity.getClass().getSimpleName() + "." + property;
|
||||
URI path = buildUri(baseUri, repository, id, property, propValId);
|
||||
links.add(new SimpleLink(rel, path));
|
||||
}
|
||||
model.addAttribute(RESOURCE, links);
|
||||
} else {
|
||||
@@ -597,12 +668,12 @@ public class RepositoryRestController
|
||||
"text/uri-list"
|
||||
}
|
||||
)
|
||||
public void updateLinks(final ServerHttpRequest request,
|
||||
UriComponentsBuilder uriBuilder,
|
||||
@PathVariable String repository,
|
||||
@PathVariable String id,
|
||||
final @PathVariable String property,
|
||||
final Model model) throws IOException {
|
||||
public void updatePropertyOfEntity(final ServerHttpRequest request,
|
||||
UriComponentsBuilder uriBuilder,
|
||||
@PathVariable String repository,
|
||||
@PathVariable String id,
|
||||
final @PathVariable String property,
|
||||
final Model model) throws IOException {
|
||||
URI baseUri = uriBuilder.build().toUri();
|
||||
|
||||
final RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
|
||||
@@ -625,7 +696,7 @@ public class RepositoryRestController
|
||||
@Override public Void handle(Object linkedEntity) {
|
||||
if (attrMeta.isCollectionLike()) {
|
||||
Collection c = new ArrayList();
|
||||
Collection current = (Collection) attrMeta.get(entity);
|
||||
Collection current = attrMeta.asCollection(entity);
|
||||
if (request.getMethod() == HttpMethod.POST && null != current) {
|
||||
c.addAll(current);
|
||||
}
|
||||
@@ -633,7 +704,7 @@ public class RepositoryRestController
|
||||
attrMeta.set(c, entity);
|
||||
} else if (attrMeta.isSetLike()) {
|
||||
Set s = new HashSet();
|
||||
Set current = (Set) attrMeta.get(entity);
|
||||
Set current = attrMeta.asSet(entity);
|
||||
if (request.getMethod() == HttpMethod.POST && null != current) {
|
||||
s.addAll(current);
|
||||
}
|
||||
@@ -641,7 +712,7 @@ public class RepositoryRestController
|
||||
attrMeta.set(s, entity);
|
||||
} else if (attrMeta.isMapLike()) {
|
||||
Map m = new HashMap();
|
||||
Map current = (Map) attrMeta.get(entity);
|
||||
Map current = attrMeta.asMap(entity);
|
||||
if (request.getMethod() == HttpMethod.POST && null != current) {
|
||||
m.putAll(current);
|
||||
}
|
||||
@@ -671,7 +742,9 @@ public class RepositoryRestController
|
||||
}
|
||||
}
|
||||
} else if (jsonMediaType.equals(incomingMediaType)) {
|
||||
final Map<String, List<Map<String, String>>> incoming = readIncoming(request, incomingMediaType, Map.class);
|
||||
final Map<String, List<Map<String, String>>> incoming = readIncoming(request,
|
||||
incomingMediaType,
|
||||
Map.class);
|
||||
for (Map<String, String> link : incoming.get(LINKS)) {
|
||||
String sLinkUri = link.get("href");
|
||||
Object o = resolveTopLevelResource(baseUri, sLinkUri);
|
||||
@@ -782,6 +855,7 @@ public class RepositoryRestController
|
||||
Object linkedEntity = linkedRepo.findOne(sChildId);
|
||||
if (null != linkedEntity) {
|
||||
Map<String, Object> entityDto = extractPropertiesLinkAware(repository,
|
||||
linkedRepoMeta.rel(),
|
||||
linkedEntity,
|
||||
linkedRepoMeta.entityMetadata(),
|
||||
baseUri);
|
||||
@@ -969,7 +1043,8 @@ public class RepositoryRestController
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private Map<String, Object> extractPropertiesLinkAware(String repository,
|
||||
private Map<String, Object> extractPropertiesLinkAware(String repoName,
|
||||
String repoRel,
|
||||
Object entity,
|
||||
EntityMetadata<AttributeMetadata> entityMetadata,
|
||||
URI baseUri) {
|
||||
@@ -988,7 +1063,7 @@ public class RepositoryRestController
|
||||
.pathSegment(attrName)
|
||||
.build()
|
||||
.toUri();
|
||||
Link l = new SimpleLink(repository + "." + entity.getClass().getSimpleName() + "." + attrName, uri);
|
||||
Link l = new SimpleLink(repoRel + "." + entity.getClass().getSimpleName() + "." + attrName, uri);
|
||||
List<Link> links = (List<Link>) entityDto.get(LINKS);
|
||||
if (null == links) {
|
||||
links = new ArrayList<Link>();
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
|
||||
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Configuration
|
||||
public class RepositoryRestMvcConfiguration {
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class ServerHttpRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.servlet.view.AbstractView;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class UriListView extends AbstractView {
|
||||
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
||||
version="2.5">
|
||||
|
||||
<context-param>
|
||||
<param-name>contextClass</param-name>
|
||||
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>org.springframework.data.rest.webmvc.RepositoryRestConfiguration</param-value>
|
||||
<param-value>
|
||||
classpath*:META-INF/spring-data-rest/**/*-export.xml
|
||||
</param-value>
|
||||
</context-param>
|
||||
|
||||
<listener>
|
||||
@@ -26,7 +24,10 @@
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration</param-value>
|
||||
<param-value>
|
||||
org.springframework.data.rest.webmvc.RepositoryRestConfiguration
|
||||
org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration
|
||||
</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
@@ -2,12 +2,9 @@ package org.springframework.data.rest.webmvc.spec
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper
|
||||
import org.codehaus.jackson.map.ser.CustomSerializerFactory
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext
|
||||
import org.springframework.data.rest.core.SimpleLink
|
||||
import org.springframework.data.rest.core.util.FluentBeanSerializer
|
||||
import org.springframework.data.rest.repository.context.ValidatingRepositoryEventListener
|
||||
import org.springframework.data.rest.test.webmvc.Address
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestConfiguration
|
||||
import org.springframework.data.rest.webmvc.RepositoryRestController
|
||||
@@ -15,26 +12,25 @@ import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.server.ServletServerHttpRequest
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.mock.web.MockServletConfig
|
||||
import org.springframework.mock.web.MockServletContext
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.ui.ExtendedModelMap
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
import org.springframework.web.util.UriComponentsBuilder
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@ContextConfiguration(classes = [RepositoryRestConfiguration, RepositoryRestMvcConfiguration, RepositorySpecConfig])
|
||||
class RepositoryRestControllerSpec extends Specification {
|
||||
|
||||
@Shared
|
||||
UriComponentsBuilder uriBuilder
|
||||
@Shared
|
||||
ObjectMapper mapper = new ObjectMapper()
|
||||
@Autowired
|
||||
URI baseUri
|
||||
@Autowired
|
||||
@Shared
|
||||
RepositoryRestController controller
|
||||
|
||||
MockHttpServletRequest createRequest(String method, String path) {
|
||||
@@ -46,6 +42,14 @@ class RepositoryRestControllerSpec extends Specification {
|
||||
}
|
||||
|
||||
def setupSpec() {
|
||||
def appCtx = new ClassPathXmlApplicationContext("classpath*:META-INF/spring-data-rest/**/*-export.xml")
|
||||
def webAppCtx = new AnnotationConfigWebApplicationContext()
|
||||
webAppCtx.setServletConfig(new MockServletConfig())
|
||||
webAppCtx.setServletContext(new MockServletContext())
|
||||
webAppCtx.setConfigLocations([RepositoryRestConfiguration.name, RepositoryRestMvcConfiguration.name] as String[])
|
||||
webAppCtx.setParent(appCtx)
|
||||
webAppCtx.afterPropertiesSet()
|
||||
controller = webAppCtx.getBean(RepositoryRestController)
|
||||
uriBuilder = UriComponentsBuilder.fromUriString("http://localhost:8080/data")
|
||||
def customSerializerFactory = new CustomSerializerFactory()
|
||||
customSerializerFactory.addSpecificMapping(SimpleLink, new FluentBeanSerializer(SimpleLink))
|
||||
@@ -68,40 +72,40 @@ class RepositoryRestControllerSpec extends Specification {
|
||||
|
||||
when: "adding an entity"
|
||||
model.clear()
|
||||
def req = createRequest("POST", "person")
|
||||
def req = createRequest("POST", "people")
|
||||
def data = mapper.writeValueAsBytes([name: "John Doe"])
|
||||
req.content = data
|
||||
controller.create(new ServletServerHttpRequest(req), uriBuilder, "person", model)
|
||||
controller.create(new ServletServerHttpRequest(req), uriBuilder, "people", model)
|
||||
|
||||
then:
|
||||
model.status == HttpStatus.CREATED
|
||||
|
||||
when: "getting specific entity"
|
||||
model.clear()
|
||||
req = createRequest("GET", "person/1")
|
||||
controller.entity(new ServletServerHttpRequest(req), uriBuilder, "person", "1", model)
|
||||
req = createRequest("GET", "people/1")
|
||||
controller.entity(new ServletServerHttpRequest(req), uriBuilder, "people", "1", model)
|
||||
|
||||
then:
|
||||
model.resource?.name == "John Doe"
|
||||
|
||||
when: "updating an entity"
|
||||
model.clear()
|
||||
req = createRequest("PUT", "person/1")
|
||||
req = createRequest("PUT", "people/1")
|
||||
data = mapper.writeValueAsBytes([name: "Johnnie Doe", version: 0])
|
||||
req.content = data
|
||||
controller.createOrUpdate(new ServletServerHttpRequest(req), uriBuilder, "person", "1", model)
|
||||
controller.createOrUpdate(new ServletServerHttpRequest(req), uriBuilder, "people", "1", model)
|
||||
|
||||
then:
|
||||
model.status == HttpStatus.NO_CONTENT
|
||||
|
||||
when: "listing available entities"
|
||||
model.clear()
|
||||
controller.listEntities(uriBuilder, "person", model)
|
||||
def personsLinks = model.resource?.links
|
||||
controller.listEntities(uriBuilder, "people", model)
|
||||
def peopleLinks = model.resource?.links
|
||||
|
||||
then:
|
||||
model.status == HttpStatus.OK
|
||||
personsLinks[0].href().toString() == "http://localhost:8080/data/person/1"
|
||||
peopleLinks[0].href().toString() == "http://localhost:8080/data/people/1"
|
||||
|
||||
when: "creating child entity"
|
||||
model.clear()
|
||||
@@ -115,18 +119,18 @@ class RepositoryRestControllerSpec extends Specification {
|
||||
|
||||
when: "linking child to parent entity"
|
||||
model.clear()
|
||||
req = createRequest("POST", "person/1/addresses")
|
||||
req = createRequest("POST", "people/1/addresses")
|
||||
req.contentType = "text/uri-list"
|
||||
data = "http://localhost:8080/data/address/1".bytes
|
||||
req.content = data
|
||||
controller.updateLinks(new ServletServerHttpRequest(req), uriBuilder, "person", "1", "addresses", model)
|
||||
controller.updatePropertyOfEntity(new ServletServerHttpRequest(req), uriBuilder, "people", "1", "addresses", model)
|
||||
|
||||
then:
|
||||
model.status == HttpStatus.CREATED
|
||||
|
||||
when: "getting property of entity"
|
||||
model.clear()
|
||||
controller.propertyOfEntity(uriBuilder, "person", "1", "addresses", model)
|
||||
controller.propertyOfEntity(uriBuilder, "people", "1", "addresses", model)
|
||||
def addrLinks = model.resource?.links
|
||||
|
||||
then:
|
||||
@@ -136,12 +140,3 @@ class RepositoryRestControllerSpec extends Specification {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class RepositorySpecConfig {
|
||||
|
||||
@Bean ValidatingRepositoryEventListener validator() {
|
||||
new ValidatingRepositoryEventListener()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class RestBuilder {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Entity
|
||||
public class Address {
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.springframework.data.rest.test.webmvc;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public interface AddressRepository extends CrudRepository<Address, Long> {
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import javax.persistence.OneToMany;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Map;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class PersonLoader implements InitializingBean {
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.repository.annotation.RestPathSegment;
|
||||
import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@RestPathSegment("person")
|
||||
@RestResource(path = "people", rel = "peeps")
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
@RestPathSegment("byName")
|
||||
@RestResource(path = "name", rel = "names")
|
||||
public List<Person> findByName(@Param("name") String name);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.springframework.validation.ValidationUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class PersonValidator implements Validator {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Entity
|
||||
public class Profile {
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.springframework.data.rest.test.webmvc;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jon@jbrisbin.com>
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public interface ProfileRepository extends CrudRepository<Profile, Long> {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user