DATAREST-523 - Re-enabled POST for collection based association resources.
We now support augmenting elements of a collection resource by using POST which previously only worked with PATCH requests. Took the chance to clean up RepositoryPropertyReferenceController by quite a bit and refactor functionality to discover the supported HTTP methods for a PersistentProperty into RootResourceInformation.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -15,12 +15,16 @@
|
||||
*/
|
||||
package org.springframework.data.rest.core.mapping;
|
||||
|
||||
import static org.springframework.data.rest.core.mapping.ResourceType.*;
|
||||
import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.repository.core.CrudMethods;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -48,19 +52,6 @@ public class CrudMethodsSupportedHttpMethods implements SupportedHttpMethods {
|
||||
this.exposedMethods = new DefaultExposureAwareCrudMethods(crudMethods);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.core.mapping.SupportedHttpMethods#supports(org.springframework.http.HttpMethod, org.springframework.data.rest.core.mapping.ResourceType)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(HttpMethod method, ResourceType type) {
|
||||
|
||||
Assert.notNull(method, "HTTP method must not be null!");
|
||||
Assert.notNull(type, "Resource type must not be null!");
|
||||
|
||||
return getMethodsFor(type).contains(method);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.core.mapping.SupportedHttpMethods#getSupportedHttpMethods(org.springframework.data.rest.core.mapping.ResourceType)
|
||||
@@ -71,18 +62,18 @@ public class CrudMethodsSupportedHttpMethods implements SupportedHttpMethods {
|
||||
Assert.notNull(resourcType, "Resource type must not be null!");
|
||||
|
||||
Set<HttpMethod> methods = new HashSet<HttpMethod>();
|
||||
methods.add(HttpMethod.OPTIONS);
|
||||
methods.add(OPTIONS);
|
||||
|
||||
switch (resourcType) {
|
||||
case COLLECTION:
|
||||
|
||||
if (exposedMethods.exposesFindAll()) {
|
||||
methods.add(HttpMethod.GET);
|
||||
methods.add(HttpMethod.HEAD);
|
||||
methods.add(GET);
|
||||
methods.add(HEAD);
|
||||
}
|
||||
|
||||
if (exposedMethods.exposesSave()) {
|
||||
methods.add(HttpMethod.POST);
|
||||
methods.add(POST);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -90,17 +81,17 @@ public class CrudMethodsSupportedHttpMethods implements SupportedHttpMethods {
|
||||
case ITEM:
|
||||
|
||||
if (exposedMethods.exposesDelete() && exposedMethods.exposesFindOne()) {
|
||||
methods.add(HttpMethod.DELETE);
|
||||
methods.add(DELETE);
|
||||
}
|
||||
|
||||
if (exposedMethods.exposesFindOne()) {
|
||||
methods.add(HttpMethod.GET);
|
||||
methods.add(HttpMethod.HEAD);
|
||||
methods.add(GET);
|
||||
methods.add(HEAD);
|
||||
}
|
||||
|
||||
if (exposedMethods.exposesSave()) {
|
||||
methods.add(HttpMethod.PUT);
|
||||
methods.add(HttpMethod.PATCH);
|
||||
methods.add(PUT);
|
||||
methods.add(PATCH);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -112,6 +103,34 @@ public class CrudMethodsSupportedHttpMethods implements SupportedHttpMethods {
|
||||
return Collections.unmodifiableSet(methods);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.core.mapping.SupportedHttpMethods#getMethodsFor(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public Set<HttpMethod> getMethodsFor(PersistentProperty<?> property) {
|
||||
|
||||
if (!property.isAssociation()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<HttpMethod> methods = new HashSet<HttpMethod>();
|
||||
|
||||
methods.add(GET);
|
||||
|
||||
if (property.isWritable() && getMethodsFor(ITEM).contains(PUT)) {
|
||||
methods.add(PUT);
|
||||
methods.add(PATCH);
|
||||
methods.add(DELETE);
|
||||
}
|
||||
|
||||
if (property.isCollectionLike() && property.isWritable()) {
|
||||
methods.add(POST);
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.rest.core.mapping;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
@@ -27,22 +28,21 @@ import org.springframework.http.HttpMethod;
|
||||
*/
|
||||
public interface SupportedHttpMethods {
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link HttpMethod} is supported for the given {@link ResourceType}.
|
||||
*
|
||||
* @param httpMethod must not be {@literal null}.
|
||||
* @param resourceType must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
boolean supports(HttpMethod method, ResourceType type);
|
||||
|
||||
/**
|
||||
* Returns the supported {@link HttpMethod}s for the given {@link ResourceType}.
|
||||
*
|
||||
* @param resourcType must not be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Set<HttpMethod> getMethodsFor(ResourceType resourcType);
|
||||
Set<HttpMethod> getMethodsFor(ResourceType type);
|
||||
|
||||
/**
|
||||
* Returns the supported {@link HttpMethod}s for the given {@link PersistentProperty}.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Set<HttpMethod> getMethodsFor(PersistentProperty<?> property);
|
||||
|
||||
/**
|
||||
* Null object to abstract the absence of any support for any HTTP method.
|
||||
@@ -64,11 +64,11 @@ public interface SupportedHttpMethods {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.rest.core.mapping.SupportedHttpMethods#supports(org.springframework.http.HttpMethod, org.springframework.data.rest.core.mapping.ResourceType)
|
||||
* @see org.springframework.data.rest.core.mapping.SupportedHttpMethods#getMethodsFor(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(HttpMethod method, ResourceType type) {
|
||||
return false;
|
||||
public Set<HttpMethod> getMethodsFor(PersistentProperty<?> property) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -15,15 +15,21 @@
|
||||
*/
|
||||
package org.springframework.data.rest.core.mapping;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.rest.core.mapping.ResourceType.*;
|
||||
import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.CrudMethods;
|
||||
@@ -91,6 +97,30 @@ public class CrudMethodsSupportedHttpMethodsUnitTests {
|
||||
assertMethodsSupported(supportedHttpMethods, ITEM, false, DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-523
|
||||
*/
|
||||
@Test
|
||||
public void exposesMethodsForProperties() {
|
||||
|
||||
MongoMappingContext context = new MongoMappingContext();
|
||||
MongoPersistentEntity<?> entity = context.getPersistentEntity(Entity.class);
|
||||
|
||||
SupportedHttpMethods methods = getSupportedHttpMethodsFor(EntityRepository.class);
|
||||
|
||||
assertThat(methods.getMethodsFor(entity.getPersistentProperty("embedded")), is(empty()));
|
||||
assertThat(methods.getMethodsFor(entity.getPersistentProperty("embeddedCollection")), is(empty()));
|
||||
|
||||
assertThat(methods.getMethodsFor(entity.getPersistentProperty("related")),
|
||||
allOf(hasItems(GET, DELETE, PATCH, PUT), not(hasItem(POST))));
|
||||
|
||||
assertThat(methods.getMethodsFor(entity.getPersistentProperty("relatedCollection")),
|
||||
hasItems(GET, DELETE, PATCH, PUT, POST));
|
||||
|
||||
assertThat(methods.getMethodsFor(entity.getPersistentProperty("readOnlyReference")),
|
||||
allOf(hasItem(GET), not(hasItems(DELETE, PATCH, PUT, POST))));
|
||||
}
|
||||
|
||||
private static SupportedHttpMethods getSupportedHttpMethodsFor(Class<?> repositoryInterface) {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
|
||||
@@ -102,12 +132,12 @@ public class CrudMethodsSupportedHttpMethodsUnitTests {
|
||||
private static void assertMethodsSupported(SupportedHttpMethods methods, ResourceType type, boolean supported,
|
||||
HttpMethod... httpMethods) {
|
||||
|
||||
Matcher<Iterable<HttpMethod>> isSupported = supported ? hasItems(httpMethods) : not(hasItems(httpMethods));
|
||||
Set<HttpMethod> result = methods.getMethodsFor(type);
|
||||
|
||||
assertThat(methods.getMethodsFor(type), isSupported);
|
||||
assertThat(result, supported ? hasItems(httpMethods) : not(hasItems(httpMethods)));
|
||||
|
||||
for (HttpMethod method : httpMethods) {
|
||||
assertThat(methods.supports(method, type), is(supported));
|
||||
if (supported) {
|
||||
assertThat(result, hasSize(httpMethods.length));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,4 +150,15 @@ public class CrudMethodsSupportedHttpMethodsUnitTests {
|
||||
@RestResource(exported = false)
|
||||
void delete(Object id);
|
||||
}
|
||||
|
||||
interface EntityRepository extends CrudRepository<Entity, Long> {}
|
||||
|
||||
class Entity {
|
||||
|
||||
Entity embedded;
|
||||
@Reference Entity related;
|
||||
List<Entity> embeddedCollection;
|
||||
@Reference List<Entity> relatedCollection;
|
||||
@ReadOnlyProperty @Reference Entity readOnlyReference;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 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.
|
||||
@@ -16,10 +16,13 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
import static org.springframework.data.rest.webmvc.RestMediaTypes.*;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -48,7 +51,6 @@ import org.springframework.data.rest.core.event.BeforeLinkSaveEvent;
|
||||
import org.springframework.data.rest.core.mapping.PropertyAwareResourceMapping;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMapping;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.ResourceType;
|
||||
import org.springframework.data.rest.core.util.Function;
|
||||
import org.springframework.data.rest.webmvc.support.BackendId;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
@@ -60,13 +62,12 @@ import org.springframework.hateoas.mvc.ControllerLinkBuilder;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
@@ -79,6 +80,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
ApplicationEventPublisherAware {
|
||||
|
||||
private static final String BASE_MAPPING = "/{repository}/{id}/{property}";
|
||||
private static final Collection<HttpMethod> AUGMENTING_METHODS = Arrays.asList(HttpMethod.PATCH, HttpMethod.POST);
|
||||
|
||||
private final Repositories repositories;
|
||||
private final ConversionService conversionService;
|
||||
@@ -105,7 +107,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
this.publisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET)
|
||||
@RequestMapping(value = BASE_MAPPING, method = GET)
|
||||
public ResponseEntity<ResourceSupport> followPropertyReference(final RootResourceInformation repoRequest,
|
||||
@BackendId Serializable id, final @PathVariable String property, final PersistentEntityResourceAssembler assembler)
|
||||
throws Exception {
|
||||
@@ -148,17 +150,12 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
return ControllerUtils.toResponseEntity(HttpStatus.OK, headers, responseResource);
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.DELETE)
|
||||
@RequestMapping(value = BASE_MAPPING, method = DELETE)
|
||||
public ResponseEntity<? extends ResourceSupport> deletePropertyReference(final RootResourceInformation repoRequest,
|
||||
@BackendId Serializable id, @PathVariable String property) throws Exception {
|
||||
|
||||
final RepositoryInvoker repoMethodInvoker = repoRequest.getInvoker();
|
||||
|
||||
// Can't delete a property if
|
||||
if (repoRequest.getSupportedMethods().supports(HttpMethod.PUT, ResourceType.ITEM)) {
|
||||
return new ResponseEntity<Resource<?>>(HttpStatus.METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
Function<ReferencedProperty, ResourceSupport> handler = new Function<ReferencedProperty, ResourceSupport>() {
|
||||
|
||||
@Override
|
||||
@@ -189,7 +186,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.GET)
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = GET)
|
||||
public ResponseEntity<ResourceSupport> followPropertyReference(final RootResourceInformation repoRequest,
|
||||
@BackendId Serializable id, @PathVariable String property, final @PathVariable String propertyId,
|
||||
final PersistentEntityResourceAssembler assembler) throws Exception {
|
||||
@@ -229,6 +226,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
} else {
|
||||
return new Resource<Object>(prop.propertyValue);
|
||||
}
|
||||
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
};
|
||||
@@ -237,8 +235,8 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
return ControllerUtils.toResponseEntity(HttpStatus.OK, headers, responseResource);
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET, produces = {
|
||||
"application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@RequestMapping(value = BASE_MAPPING, method = GET,
|
||||
produces = { SPRING_DATA_COMPACT_JSON_VALUE, TEXT_URI_LIST_VALUE })
|
||||
public ResponseEntity<ResourceSupport> followPropertyReferenceCompact(RootResourceInformation repoRequest,
|
||||
@BackendId Serializable id, @PathVariable String property, PersistentEntityResourceAssembler assembler)
|
||||
throws Exception {
|
||||
@@ -286,10 +284,8 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
return ControllerUtils.toResponseEntity(HttpStatus.OK, null, new Resource<Object>(EMPTY_RESOURCE_LIST, links));
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING, //
|
||||
method = { RequestMethod.PATCH, RequestMethod.PUT }, //
|
||||
consumes = { "application/json", "application/x-spring-data-compact+json", "text/uri-list" })
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING, method = { PATCH, PUT, POST }, //
|
||||
consumes = { MediaType.APPLICATION_JSON_VALUE, SPRING_DATA_COMPACT_JSON_VALUE, TEXT_URI_LIST_VALUE })
|
||||
public ResponseEntity<? extends ResourceSupport> createPropertyReference(
|
||||
final RootResourceInformation resourceInformation, final HttpMethod requestMethod, final @RequestBody(
|
||||
required = false) Resources<Object> incoming, @BackendId Serializable id, @PathVariable String property)
|
||||
@@ -307,37 +303,27 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
|
||||
if (prop.property.isCollectionLike()) {
|
||||
|
||||
Collection<Object> coll = CollectionFactory.createCollection(propertyType, 0);
|
||||
|
||||
// Either load the exist collection to add to it (PATCH)
|
||||
if (HttpMethod.PATCH.equals(requestMethod)) {
|
||||
coll = (Collection<Object>) prop.propertyValue;
|
||||
}
|
||||
Collection<Object> collection = AUGMENTING_METHODS.contains(requestMethod) ? (Collection<Object>) prop.propertyValue
|
||||
: CollectionFactory.createCollection(propertyType, 0);
|
||||
|
||||
// Add to the existing collection
|
||||
for (Link l : source.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l);
|
||||
coll.add(propVal);
|
||||
collection.add(loadPropertyValue(prop.propertyType, l));
|
||||
}
|
||||
|
||||
prop.accessor.setProperty(prop.property, coll);
|
||||
prop.accessor.setProperty(prop.property, collection);
|
||||
|
||||
} else if (prop.property.isMap()) {
|
||||
|
||||
Map<String, Object> m = CollectionFactory.createMap(propertyType, 0);
|
||||
|
||||
// Either load the exist collection to add to it (PATCH)
|
||||
if (HttpMethod.PATCH.equals(requestMethod)) {
|
||||
m = (Map<String, Object>) prop.propertyValue;
|
||||
}
|
||||
Map<String, Object> map = AUGMENTING_METHODS.contains(requestMethod) ? (Map<String, Object>) prop.propertyValue
|
||||
: CollectionFactory.<String, Object> createMap(propertyType, 0);
|
||||
|
||||
// Add to the existing collection
|
||||
for (Link l : source.getLinks()) {
|
||||
Object propVal = loadPropertyValue(prop.propertyType, l);
|
||||
m.put(l.getRel(), propVal);
|
||||
map.put(l.getRel(), loadPropertyValue(prop.propertyType, l));
|
||||
}
|
||||
|
||||
prop.accessor.setProperty(prop.property, m);
|
||||
prop.accessor.setProperty(prop.property, map);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -368,19 +354,13 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
@RequestMapping(value = BASE_MAPPING + "/{propertyId}", method = DELETE)
|
||||
public ResponseEntity<ResourceSupport> deletePropertyReferenceId(final RootResourceInformation repoRequest,
|
||||
@BackendId Serializable id, @PathVariable String property, final @PathVariable String propertyId)
|
||||
throws Exception {
|
||||
|
||||
final RepositoryInvoker invoker = repoRequest.getInvoker();
|
||||
|
||||
// Property can't be deleted if root resource can't be updated
|
||||
if (!repoRequest.getSupportedMethods().supports(HttpMethod.PUT, ResourceType.ITEM)) {
|
||||
throw new HttpRequestMethodNotSupportedException(HttpMethod.DELETE.name());
|
||||
}
|
||||
|
||||
Function<ReferencedProperty, ResourceSupport> handler = new Function<ReferencedProperty, ResourceSupport>() {
|
||||
|
||||
@Override
|
||||
@@ -443,12 +423,6 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
private ResourceSupport doWithReferencedProperty(RootResourceInformation resourceInformation, Serializable id,
|
||||
String propertyPath, Function<ReferencedProperty, ResourceSupport> handler, HttpMethod method) throws Exception {
|
||||
|
||||
RepositoryInvoker invoker = resourceInformation.getInvoker();
|
||||
|
||||
if (!resourceInformation.getSupportedMethods().supports(method, ResourceType.ITEM)) {
|
||||
throw new HttpRequestMethodNotSupportedException(method.name());
|
||||
}
|
||||
|
||||
ResourceMetadata metadata = resourceInformation.getResourceMetadata();
|
||||
PropertyAwareResourceMapping mapping = metadata.getProperty(propertyPath);
|
||||
|
||||
@@ -456,13 +430,16 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
PersistentProperty<?> property = mapping.getProperty();
|
||||
resourceInformation.verifySupportedMethod(method, property);
|
||||
|
||||
RepositoryInvoker invoker = resourceInformation.getInvoker();
|
||||
Object domainObj = invoker.invokeFindOne(id);
|
||||
|
||||
if (null == domainObj) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
PersistentProperty<?> property = mapping.getProperty();
|
||||
PersistentPropertyAccessor accessor = property.getOwner().getPropertyAccessor(domainObj);
|
||||
return handler.apply(new ReferencedProperty(property, accessor.getProperty(property), accessor));
|
||||
}
|
||||
@@ -484,5 +461,4 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
this.entity = repositories.getPersistentEntity(propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -25,6 +25,9 @@ import org.springframework.http.MediaType;
|
||||
*/
|
||||
public class RestMediaTypes {
|
||||
|
||||
public static final String SPRING_DATA_COMPACT_JSON_VALUE = "application/x-spring-data-compact+json";
|
||||
public static final String TEXT_URI_LIST_VALUE = "text/uri-list";
|
||||
|
||||
public static final MediaType HAL_JSON = MediaTypes.HAL_JSON;
|
||||
|
||||
public static final MediaType JSON_PATCH_JSON = MediaType.valueOf("application/json-patch+json");
|
||||
@@ -33,5 +36,7 @@ public class RestMediaTypes {
|
||||
public static final MediaType SCHEMA_JSON = MediaType.valueOf("application/schema+json");
|
||||
|
||||
public static final MediaType SPRING_DATA_VERBOSE_JSON = MediaType.valueOf("application/x-spring-data-verbose+json");
|
||||
public static final MediaType SPRING_DATA_COMPACT_JSON = MediaType.valueOf("application/x-spring-data-compact+json");
|
||||
public static final MediaType SPRING_DATA_COMPACT_JSON = MediaType.valueOf(SPRING_DATA_COMPACT_JSON_VALUE);
|
||||
|
||||
public static final MediaType TEXT_URI_LIST = MediaType.valueOf(TEXT_URI_LIST_VALUE);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.repository.support.RepositoryInvoker;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.ResourceType;
|
||||
@@ -92,25 +93,57 @@ public class RootResourceInformation {
|
||||
public void verifySupportedMethod(HttpMethod httpMethod, ResourceType resourceType)
|
||||
throws HttpRequestMethodNotSupportedException, ResourceNotFoundException {
|
||||
|
||||
Assert.notNull(httpMethod, "HTTP method must not be null!");
|
||||
Assert.notNull(resourceType, "Resource type must not be null!");
|
||||
|
||||
if (!resourceMetadata.isExported()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
Assert.notNull(httpMethod, "HTTP method must not be null!");
|
||||
Assert.notNull(resourceType, "Resource type must not be null!");
|
||||
|
||||
SupportedHttpMethods httpMethods = resourceMetadata.getSupportedHttpMethods();
|
||||
Collection<HttpMethod> supportedMethods = httpMethods.getMethodsFor(resourceType);
|
||||
|
||||
if (!supportedMethods.contains(httpMethod)) {
|
||||
|
||||
Set<String> stringMethods = new HashSet<String>();
|
||||
|
||||
for (HttpMethod supportedMethod : supportedMethods) {
|
||||
stringMethods.add(supportedMethod.name());
|
||||
}
|
||||
|
||||
throw new HttpRequestMethodNotSupportedException(httpMethod.name(), stringMethods);
|
||||
reject(httpMethod, supportedMethods);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the given {@link HttpMethod} is supported for the given {@link PersistentProperty}.
|
||||
*
|
||||
* @param httpMethod must not be {@literal null}.
|
||||
* @param property must not be {@literal null}.
|
||||
* @throws ResourceNotFoundException if the repository is not exported at all.
|
||||
* @throws HttpRequestMethodNotSupportedException if the {@link PersistentProperty} does not support the given
|
||||
* {@link HttpMethod}. Will contain all supported methods as indicators for clients.
|
||||
*/
|
||||
public void verifySupportedMethod(HttpMethod httpMethod, PersistentProperty<?> property)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
|
||||
Assert.notNull(httpMethod, "HTTP method must not be null!");
|
||||
Assert.notNull(property, "Resource type must not be null!");
|
||||
|
||||
if (!resourceMetadata.isExported()) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
SupportedHttpMethods httpMethods = resourceMetadata.getSupportedHttpMethods();
|
||||
Collection<HttpMethod> supportedMethods = httpMethods.getMethodsFor(property);
|
||||
|
||||
if (!supportedMethods.contains(httpMethod)) {
|
||||
reject(httpMethod, supportedMethods);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reject(HttpMethod method, Collection<HttpMethod> supported)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
|
||||
Set<String> stringMethods = new HashSet<String>(supported.size());
|
||||
|
||||
for (HttpMethod supportedMethod : supported) {
|
||||
stringMethods.add(supportedMethod.name());
|
||||
}
|
||||
|
||||
throw new HttpRequestMethodNotSupportedException(method.name(), stringMethods);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -17,14 +17,14 @@ package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.rest.core.mapping.ResourceType.*;
|
||||
import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.rest.core.mapping.ResourceType;
|
||||
import org.springframework.data.rest.core.mapping.SupportedHttpMethods;
|
||||
import org.springframework.data.rest.webmvc.jpa.Address;
|
||||
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -46,7 +46,7 @@ public class RootResourceInformationIntegrationTests extends AbstractControllerI
|
||||
public void getIsNotSupportedIfFindAllIsNotExported() {
|
||||
|
||||
SupportedHttpMethods supportedMethods = getResourceInformation(Address.class).getSupportedMethods();
|
||||
assertThat(supportedMethods.supports(HttpMethod.GET, ResourceType.COLLECTION), is(false));
|
||||
assertThat(supportedMethods.getMethodsFor(COLLECTION), not(hasItem(GET)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,6 +56,6 @@ public class RootResourceInformationIntegrationTests extends AbstractControllerI
|
||||
public void postIsNotSupportedIfSaveIsNotExported() {
|
||||
|
||||
SupportedHttpMethods supportedMethods = getResourceInformation(Address.class).getSupportedMethods();
|
||||
assertThat(supportedMethods.supports(HttpMethod.POST, ResourceType.COLLECTION), is(false));
|
||||
assertThat(supportedMethods.getMethodsFor(COLLECTION), not(hasItem(POST)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,26 +415,6 @@ public class JpaWebTests extends CommonWebTests {
|
||||
assertJsonPathEquals("$.firstName", "Bilbo", overwrittenResponse);
|
||||
}
|
||||
|
||||
private List<Link> preparePersonResources(Person primary, Person... persons) throws Exception {
|
||||
|
||||
Link peopleLink = client.discoverUnique("people");
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
MockHttpServletResponse primaryResponse = postAndGet(peopleLink, mapper.writeValueAsString(primary),
|
||||
MediaType.APPLICATION_JSON);
|
||||
links.add(client.assertHasLinkWithRel("siblings", primaryResponse));
|
||||
|
||||
for (Person person : persons) {
|
||||
|
||||
String payload = mapper.writeValueAsString(person);
|
||||
MockHttpServletResponse response = postAndGet(peopleLink, payload, MediaType.APPLICATION_JSON);
|
||||
|
||||
links.add(client.assertHasLinkWithRel(Link.REL_SELF, response));
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-217
|
||||
*/
|
||||
@@ -631,6 +611,50 @@ public class JpaWebTests extends CommonWebTests {
|
||||
andExpect(status().isIAmATeapot());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-523
|
||||
*/
|
||||
@Test
|
||||
public void augmentsCollectionAssociationUsingPost() throws Exception {
|
||||
|
||||
List<Link> links = preparePersonResources(new Person("Frodo", "Baggins"), //
|
||||
new Person("Bilbo", "Baggins"));
|
||||
|
||||
Link frodosSiblingsLink = links.get(0).expand();
|
||||
Link bilboLink = links.get(1);
|
||||
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
|
||||
mvc.perform(post(frodosSiblingsLink.getHref()).//
|
||||
content(bilboLink.getHref()).//
|
||||
contentType(TEXT_URI_LIST)).//
|
||||
andExpect(status().isNoContent());
|
||||
|
||||
mvc.perform(get(frodosSiblingsLink.getHref())).//
|
||||
andExpect(jsonPath("$._embedded.people", hasSize(i)));
|
||||
}
|
||||
}
|
||||
|
||||
private List<Link> preparePersonResources(Person primary, Person... persons) throws Exception {
|
||||
|
||||
Link peopleLink = client.discoverUnique("people");
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
|
||||
MockHttpServletResponse primaryResponse = postAndGet(peopleLink, mapper.writeValueAsString(primary),
|
||||
MediaType.APPLICATION_JSON);
|
||||
links.add(client.assertHasLinkWithRel("siblings", primaryResponse));
|
||||
|
||||
for (Person person : persons) {
|
||||
|
||||
String payload = mapper.writeValueAsString(person);
|
||||
MockHttpServletResponse response = postAndGet(peopleLink, payload, MediaType.APPLICATION_JSON);
|
||||
|
||||
links.add(client.assertHasLinkWithRel(Link.REL_SELF, response));
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the {@link Person} resource the given link points to contains siblings with the given names.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user