DATAREST-1499, DATAREST-1500 - Cleanups.
Moved off Spring Framework deprecations for HttpMessageNotReadableException. This required some rearrangements of method signatures for types (hopefully) exclusively used by internal abstractions (some public, but not very friendly for user extension in the first place). Switched to consistent use of Pageable.unpaged() instead of null. Switched to the use of new factory methods in Spring HATEOAS. Some Java 8 based improvements in request handling to simplify the implementation code. Deprecation of code that got obsolete due to the use of the factory methods. Moved off some deprecations in Jackson APIs. Removed a couple of unused imports. General avoidance of common warnings. Suppression where needed. Removed dead code in configuration.
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.rest.core.mapping;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -30,7 +30,8 @@ public interface OrderRepository extends CrudRepository<Order, UUID> {
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
<S extends Order> S save(S entity);
|
||||
@SuppressWarnings("unchecked")
|
||||
Order save(Order entity);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
|
||||
@@ -113,7 +113,6 @@ public class RepositoryMethodResourceMappingUnitTests {
|
||||
}
|
||||
|
||||
@Test // DATAREST-467
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void returnsDomainTypeAsProjectionSourceType() throws Exception {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLastname", String.class);
|
||||
|
||||
@@ -25,10 +25,10 @@ import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.support.RepositoryInvoker;
|
||||
@@ -116,13 +116,14 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll
|
||||
@Test // DATAREST-330
|
||||
public void exposesHeadForCollectionResourceIfExported() throws Exception {
|
||||
ResponseEntity<?> entity = controller.headCollectionResource(getResourceInformation(Person.class),
|
||||
new DefaultedPageable(null, false));
|
||||
new DefaultedPageable(Pageable.unpaged(), false));
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class) // DATAREST-330
|
||||
public void doesNotExposeHeadForCollectionResourceIfNotExported() throws Exception {
|
||||
controller.headCollectionResource(getResourceInformation(CreditCard.class), new DefaultedPageable(null, false));
|
||||
controller.headCollectionResource(getResourceInformation(CreditCard.class),
|
||||
new DefaultedPageable(Pageable.unpaged(), false));
|
||||
}
|
||||
|
||||
@Test // DATAREST-330
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -81,7 +79,7 @@ class AbstractRepositoryRestController {
|
||||
} else if (source instanceof Iterable) {
|
||||
return entitiesToResources((Iterable<Object>) source, assembler, domainType);
|
||||
} else {
|
||||
return new CollectionModel(EMPTY_RESOURCE_LIST);
|
||||
return CollectionModel.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +101,7 @@ class AbstractRepositoryRestController {
|
||||
if (!entities.iterator().hasNext()) {
|
||||
|
||||
List<Object> content = Arrays.<Object> asList(WRAPPERS.emptyCollectionOf(domainType));
|
||||
return new CollectionModel<Object>(content, getDefaultSelfLink());
|
||||
return CollectionModel.of(content, getDefaultSelfLink());
|
||||
}
|
||||
|
||||
List<EntityModel<Object>> resources = new ArrayList<EntityModel<Object>>();
|
||||
@@ -112,7 +110,7 @@ class AbstractRepositoryRestController {
|
||||
resources.add(obj == null ? null : assembler.toModel(obj));
|
||||
}
|
||||
|
||||
return new CollectionModel<EntityModel<Object>>(resources, getDefaultSelfLink());
|
||||
return CollectionModel.of(resources, getDefaultSelfLink());
|
||||
}
|
||||
|
||||
protected Link getDefaultSelfLink() {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.rest.webmvc;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -31,7 +32,12 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class ControllerUtils {
|
||||
|
||||
public static final Iterable<EntityModel<?>> EMPTY_RESOURCE_LIST = Collections.emptyList();
|
||||
/**
|
||||
* Use {@link CollectionModel#empty()} instead.
|
||||
*
|
||||
* @deprecated since 3.3
|
||||
*/
|
||||
@Deprecated public static final Iterable<EntityModel<?>> EMPTY_RESOURCE_LIST = Collections.emptyList();
|
||||
|
||||
public static <R extends RepresentationModel<?>> ResponseEntity<RepresentationModel<?>> toResponseEntity(
|
||||
HttpStatus status, HttpHeaders headers, Optional<R> resource) {
|
||||
|
||||
@@ -24,10 +24,10 @@ import java.util.List;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.server.core.EmbeddedWrapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -65,6 +65,7 @@ public class PersistentEntityResource extends EntityModel<Object> {
|
||||
* @param links must not be {@literal null}.
|
||||
* @param embeddeds can be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private PersistentEntityResource(PersistentEntity<?, ?> entity, Object content, Iterable<Link> links,
|
||||
Iterable<EmbeddedWrapper> embeddeds, boolean isNew, boolean nested) {
|
||||
|
||||
@@ -205,6 +206,7 @@ public class PersistentEntityResource extends EntityModel<Object> {
|
||||
|
||||
private static class NoLinksResources<T> extends CollectionModel<T> {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public NoLinksResources(Iterable<T> content) {
|
||||
super(content);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import static org.springframework.http.HttpMethod.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -199,16 +198,16 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
Iterable<?> results = pageable.getPageable() != null ? invoker.invokeFindAll(pageable.getPageable())
|
||||
Iterable<?> results = pageable.getPageable() != null //
|
||||
? invoker.invokeFindAll(pageable.getPageable()) //
|
||||
: invoker.invokeFindAll(sort);
|
||||
|
||||
ResourceMetadata metadata = resourceInformation.getResourceMetadata();
|
||||
Optional<Link> baseLink = Optional.of(entityLinks.linkToPagedResource(resourceInformation.getDomainType(),
|
||||
pageable.isDefault() ? null : pageable.getPageable()));
|
||||
|
||||
CollectionModel<?> result = toCollectionModel(results, assembler, metadata.getDomainType(), baseLink);
|
||||
result.add(getCollectionResourceLinks(resourceInformation, pageable));
|
||||
return result;
|
||||
return toCollectionModel(results, assembler, metadata.getDomainType(), baseLink)
|
||||
.add(getCollectionResourceLinks(resourceInformation, pageable));
|
||||
}
|
||||
|
||||
private Links getCollectionResourceLinks(RootResourceInformation resourceInformation, DefaultedPageable pageable) {
|
||||
@@ -226,7 +225,6 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET,
|
||||
produces = { "application/x-spring-data-compact+json", "text/uri-list" })
|
||||
public CollectionModel<?> getCollectionResourceCompact(@QuerydslPredicate RootResourceInformation resourceinformation,
|
||||
@@ -234,18 +232,17 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
|
||||
throws ResourceNotFoundException, HttpRequestMethodNotSupportedException {
|
||||
|
||||
CollectionModel<?> resources = getCollectionResource(resourceinformation, pageable, sort, assembler);
|
||||
Links links = resources.getLinks();
|
||||
|
||||
for (EntityModel<?> resource : ((CollectionModel<EntityModel<?>>) resources).getContent()) {
|
||||
PersistentEntityResource persistentEntityResource = (PersistentEntityResource) resource;
|
||||
links = links.and(resourceLink(resourceinformation, persistentEntityResource));
|
||||
}
|
||||
Links links = resources.getContent().stream() //
|
||||
.map(PersistentEntityResource.class::cast) //
|
||||
.map(it -> resourceLink(resourceinformation, it)) //
|
||||
.reduce(resources.getLinks(), Links::and, Links::and);
|
||||
|
||||
if (resources instanceof PagedModel) {
|
||||
return new PagedModel<Object>(Collections.emptyList(), ((PagedModel<?>) resources).getMetadata(), links);
|
||||
} else {
|
||||
return new CollectionModel<Object>(Collections.emptyList(), links);
|
||||
}
|
||||
CollectionModel<?> model = resources instanceof PagedModel //
|
||||
? PagedModel.empty(((PagedModel<?>) resources).getMetadata()) //
|
||||
: CollectionModel.empty();
|
||||
|
||||
return model.add(links);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -252,7 +251,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
|
||||
@RequestBody(required = false) CollectionModel<Object> incoming, @BackendId Serializable id,
|
||||
@PathVariable String property) throws Exception {
|
||||
|
||||
CollectionModel<Object> source = incoming == null ? new CollectionModel<Object>(Collections.emptyList()) : incoming;
|
||||
CollectionModel<Object> source = incoming == null ? CollectionModel.empty() : incoming;
|
||||
RepositoryInvoker invoker = resourceInformation.getInvoker();
|
||||
|
||||
Function<ReferencedProperty, RepresentationModel<?>> handler = prop -> {
|
||||
|
||||
@@ -15,8 +15,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.net.URISyntaxException;
|
||||
@@ -266,7 +264,7 @@ class RepositorySearchController extends AbstractRepositoryRestController {
|
||||
links.add(resourceLink(resourceInformation, res));
|
||||
}
|
||||
|
||||
return new CollectionModel<EntityModel<?>>(EMPTY_RESOURCE_LIST, links);
|
||||
return CollectionModel.empty(links);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.data.rest.webmvc.RestMediaTypes;
|
||||
import org.springframework.data.rest.webmvc.json.DomainObjectReader;
|
||||
import org.springframework.data.rest.webmvc.json.patch.JsonPatchPatchConverter;
|
||||
import org.springframework.data.rest.webmvc.json.patch.Patch;
|
||||
import org.springframework.data.rest.webmvc.util.InputStreamHttpInputMessage;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -95,7 +96,7 @@ class JsonPatchHandler {
|
||||
return reader.read(source, existingObject, mapper);
|
||||
}
|
||||
|
||||
<T> T applyPut(ObjectNode source, T existingObject) {
|
||||
<T> T applyPut(ObjectNode source, T existingObject) throws Exception {
|
||||
return reader.readPut(source, existingObject, mapper);
|
||||
}
|
||||
|
||||
@@ -112,7 +113,8 @@ class JsonPatchHandler {
|
||||
return new JsonPatchPatchConverter(mapper).convert(mapper.readTree(source));
|
||||
} catch (Exception o_O) {
|
||||
throw new HttpMessageNotReadableException(
|
||||
String.format("Could not read PATCH operations! Expected %s!", RestMediaTypes.JSON_PATCH_JSON), o_O);
|
||||
String.format("Could not read PATCH operations! Expected %s!", RestMediaTypes.JSON_PATCH_JSON), o_O,
|
||||
InputStreamHttpInputMessage.of(source));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -23,7 +22,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
@@ -33,7 +31,6 @@ import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
@@ -43,7 +40,6 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.data.auditing.AuditableBeanWrapperFactory;
|
||||
import org.springframework.data.auditing.MappingAuditableBeanWrapperFactory;
|
||||
@@ -410,21 +406,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
new ValueTypeSchemaPropertyCustomizerFactory(repositoryInvokerFactory(defaultConversionService())));
|
||||
}
|
||||
|
||||
private final Properties lookupDefaultMessages() {
|
||||
|
||||
try {
|
||||
|
||||
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
|
||||
propertiesFactoryBean.setLocation(new ClassPathResource("rest-default-messages.properties"));
|
||||
propertiesFactoryBean.afterPropertiesSet();
|
||||
|
||||
return propertiesFactoryBean.getObject();
|
||||
|
||||
} catch (IOException o_O) {
|
||||
throw new IllegalStateException("Unable to resolve default rest-default-messages.properties!", o_O);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Jackson {@link ObjectMapper} used internally.
|
||||
*
|
||||
|
||||
@@ -15,25 +15,25 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.convert;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Converter} to render all {@link Link}s contained in a {@link ResourceSupport} as {@code text/uri-list} and
|
||||
@@ -43,7 +43,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class UriListHttpMessageConverter implements HttpMessageConverter<RepresentationModel> {
|
||||
public class UriListHttpMessageConverter implements HttpMessageConverter<RepresentationModel<?>> {
|
||||
|
||||
private static final List<MediaType> MEDIA_TYPES = new ArrayList<MediaType>();
|
||||
|
||||
@@ -57,10 +57,10 @@ public class UriListHttpMessageConverter implements HttpMessageConverter<Represe
|
||||
*/
|
||||
@Override
|
||||
public boolean canRead(Class<?> clazz, MediaType mediaType) {
|
||||
if (null == mediaType) {
|
||||
return false;
|
||||
}
|
||||
return RepresentationModel.class.isAssignableFrom(clazz) && mediaType.getSubtype().contains("uri-list");
|
||||
|
||||
return null != mediaType //
|
||||
&& RepresentationModel.class.isAssignableFrom(clazz) //
|
||||
&& mediaType.getSubtype().contains("uri-list");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -86,28 +86,17 @@ public class UriListHttpMessageConverter implements HttpMessageConverter<Represe
|
||||
* @see org.springframework.http.converter.HttpMessageConverter#read(java.lang.Class, org.springframework.http.HttpInputMessage)
|
||||
*/
|
||||
@Override
|
||||
public RepresentationModel read(Class<? extends RepresentationModel> clazz, HttpInputMessage inputMessage)
|
||||
public RepresentationModel<?> read(Class<? extends RepresentationModel<?>> clazz, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputMessage.getBody()))) {
|
||||
|
||||
Scanner scanner = new Scanner(inputMessage.getBody());
|
||||
Links links = reader.lines() //
|
||||
.map(Link::of) //
|
||||
.reduce(Links.NONE, Links::and, Links::and);
|
||||
|
||||
try {
|
||||
|
||||
while (scanner.hasNextLine()) {
|
||||
|
||||
String line = scanner.nextLine();
|
||||
if (StringUtils.hasText(line)) {
|
||||
links.add(Link.of(line));
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
scanner.close();
|
||||
return CollectionModel.empty(links);
|
||||
}
|
||||
|
||||
return new CollectionModel<Object>(Collections.emptyList(), links);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -115,7 +104,7 @@ public class UriListHttpMessageConverter implements HttpMessageConverter<Represe
|
||||
* @see org.springframework.http.converter.HttpMessageConverter#write(java.lang.Object, org.springframework.http.MediaType, org.springframework.http.HttpOutputMessage)
|
||||
*/
|
||||
@Override
|
||||
public void write(RepresentationModel resource, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
public void write(RepresentationModel<?> resource, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputMessage.getBody()));
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.data.mapping.SimplePropertyHandler;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.rest.webmvc.mapping.Associations;
|
||||
import org.springframework.data.rest.webmvc.util.InputStreamHttpInputMessage;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
@@ -88,7 +89,7 @@ public class DomainObjectReader {
|
||||
try {
|
||||
return doMerge((ObjectNode) mapper.readTree(source), target, mapper);
|
||||
} catch (Exception o_O) {
|
||||
throw new HttpMessageNotReadableException("Could not read payload!", o_O);
|
||||
throw new HttpMessageNotReadableException("Could not read payload!", o_O, InputStreamHttpInputMessage.of(source));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,24 +102,14 @@ public class DomainObjectReader {
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T readPut(final ObjectNode source, T target, final ObjectMapper mapper) {
|
||||
public <T> T readPut(final ObjectNode source, T target, final ObjectMapper mapper) throws Exception {
|
||||
|
||||
Assert.notNull(source, "ObjectNode must not be null!");
|
||||
Assert.notNull(target, "Existing object instance must not be null!");
|
||||
Assert.notNull(mapper, "ObjectMapper must not be null!");
|
||||
|
||||
Class<? extends Object> type = target.getClass();
|
||||
|
||||
entities.getRequiredPersistentEntity(type);
|
||||
|
||||
try {
|
||||
|
||||
Object intermediate = mapper.readerFor(target.getClass()).readValue(source);
|
||||
return (T) mergeForPut(intermediate, target, mapper);
|
||||
|
||||
} catch (Exception o_O) {
|
||||
throw new HttpMessageNotReadableException("Could not read payload!", o_O);
|
||||
}
|
||||
Object intermediate = mapper.readerFor(target.getClass()).readValue(source);
|
||||
return (T) mergeForPut(intermediate, target, mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,6 +177,17 @@ public class DomainObjectReader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for internal use. To be removed in 3.4.
|
||||
*
|
||||
* @param <T>
|
||||
* @param source
|
||||
* @param target
|
||||
* @param mapper
|
||||
* @return
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public <T> T merge(ObjectNode source, T target, ObjectMapper mapper) {
|
||||
|
||||
try {
|
||||
|
||||
@@ -69,7 +69,7 @@ public class JacksonMetadata implements Iterable<BeanPropertyDefinition> {
|
||||
BeanDescription description = serializationConfig.introspect(javaType);
|
||||
|
||||
this.definitions = description.findProperties();
|
||||
this.isValue = description.findJsonValueMethod() != null;
|
||||
this.isValue = description.findJsonValueAccessor() != null;
|
||||
|
||||
DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
|
||||
JavaType deserializationType = deserializationConfig.constructType(type);
|
||||
|
||||
@@ -195,6 +195,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
EntityModel<Object> resourceToRender = new EntityModel<Object>(resource.getContent(), links) {
|
||||
|
||||
@JsonUnwrapped
|
||||
@@ -689,7 +690,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
ResourceMetadata metadata = associations.getMetadataFor(value.getTargetClass());
|
||||
Links links = metadata.isExported() ? collector.getLinksFor(target) : Links.NONE;
|
||||
|
||||
EntityModel<TargetAware> resource = invoker.invokeProcessorsFor(new EntityModel<TargetAware>(value, links));
|
||||
EntityModel<TargetAware> resource = invoker.invokeProcessorsFor(EntityModel.of(value, links));
|
||||
|
||||
return new ProjectionResource(resource.getContent(), resource.getLinks());
|
||||
}
|
||||
@@ -697,6 +698,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
|
||||
static class ProjectionResource extends EntityModel<ProjectionResourceContent> {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
ProjectionResource(TargetAware projection, Iterable<Link> links) {
|
||||
super(new ProjectionResourceContent(projection, projection.getClass().getInterfaces()[0]), links);
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ import org.springframework.data.mapping.context.PersistentEntities;
|
||||
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.hateoas.server.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.server.EntityLinks;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
@@ -43,7 +43,7 @@ public class NestedLinkCollectingAssociationHandler implements SimpleAssociation
|
||||
|
||||
private final EntityLinks entityLinks;
|
||||
private final PersistentEntities entities;
|
||||
private final PersistentPropertyAccessor accessor;
|
||||
private final PersistentPropertyAccessor<?> accessor;
|
||||
private final ResourceMappings mappings;
|
||||
|
||||
private final @Getter List<Link> links = new ArrayList<Link>();
|
||||
@@ -70,10 +70,10 @@ public class NestedLinkCollectingAssociationHandler implements SimpleAssociation
|
||||
|
||||
links.add(entityLinks.linkForItemResource(element.getClass(), identifierAccessor.getIdentifier())
|
||||
.withRel(propertyMapping.getRel()));
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
PersistentEntity<?, ?> entity = entities.getRequiredPersistentEntity(propertyValue.getClass());
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(propertyValue);
|
||||
|
||||
|
||||
@@ -15,38 +15,28 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.support;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
/**
|
||||
* Value object to capture a {@link Pageable} as well it is the default one configured.
|
||||
* Value object to capture a {@link Pageable} as well whether it is the default one configured.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
public class DefaultedPageable {
|
||||
|
||||
private final Pageable pageable;
|
||||
private final boolean isDefault;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultedPageable} with the given {@link Pageable} and default flag.
|
||||
*
|
||||
* @param pageable can be {@literal null}.
|
||||
* @param isDefault
|
||||
*/
|
||||
public DefaultedPageable(Pageable pageable, boolean isDefault) {
|
||||
|
||||
this.pageable = pageable;
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delegate {@link Pageable}.
|
||||
*
|
||||
* @return can be {@literal null}.
|
||||
*/
|
||||
public Pageable getPageable() {
|
||||
return pageable;
|
||||
}
|
||||
private final @NonNull Pageable pageable;
|
||||
private final @Getter(value = AccessLevel.NONE) boolean isDefault;
|
||||
|
||||
/**
|
||||
* Returns whether the contained {@link Pageable} is the default one configured.
|
||||
@@ -56,4 +46,14 @@ public class DefaultedPageable {
|
||||
public boolean isDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Pageable#unpaged()} if the contained {@link Pageable} is the default one.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
* @since 3.3
|
||||
*/
|
||||
public Pageable unpagedIfDefault() {
|
||||
return isDefault ? Pageable.unpaged() : pageable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public final class ETag {
|
||||
Assert.notNull(entity, "PersistentEntity must not be null!");
|
||||
Assert.notNull(bean, "Target bean must not be null!");
|
||||
|
||||
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(bean);
|
||||
PersistentPropertyAccessor<?> accessor = entity.getPropertyAccessor(bean);
|
||||
|
||||
return Optional.ofNullable(entity.getVersionProperty())//
|
||||
.map(it -> accessor.getProperty(it))//
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.util;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
|
||||
/**
|
||||
* {@link HttpInputMessage} based on a plain {@link InputStream}, i.e. exposing no headers.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
public class InputStreamHttpInputMessage implements HttpInputMessage {
|
||||
|
||||
private final @Getter InputStream body;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.http.HttpMessage#getHeaders()
|
||||
*/
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return HttpHeaders.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BasePathAwareHandlerMapping}.
|
||||
@@ -54,7 +54,7 @@ public class BasePathAwareHandlerMappingUnitTests {
|
||||
ProxyFactory factory = new ProxyFactory(source);
|
||||
Object proxy = factory.getProxy();
|
||||
|
||||
assertThat(ClassUtils.isCglibProxy(proxy)).isTrue();
|
||||
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
|
||||
|
||||
return proxy.getClass();
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.server.core.EmbeddedWrapper;
|
||||
import org.springframework.hateoas.server.core.EmbeddedWrappers;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class PersistentEntityResourceUnitTests {
|
||||
|
||||
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
|
||||
EmbeddedWrapper wrapper = wrappers.wrap("Embedded", LinkRelation.of("foo"));
|
||||
this.resources = new CollectionModel<EmbeddedWrapper>(Collections.singleton(wrapper));
|
||||
this.resources = CollectionModel.of(Collections.singleton(wrapper));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAREST-317
|
||||
|
||||
@@ -44,8 +44,8 @@ import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.mapping.ResourceType;
|
||||
import org.springframework.data.rest.core.mapping.SupportedHttpMethods;
|
||||
import org.springframework.data.web.PagedResourcesAssembler;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
@@ -84,7 +84,7 @@ public class RepositoryPropertyReferenceControllerUnitTests {
|
||||
doReturn(new Sample()).when(invoker).invokeSave(any(Object.class));
|
||||
|
||||
RootResourceInformation information = new RootResourceInformation(metadata, entity, invoker);
|
||||
CollectionModel<Object> request = new CollectionModel<Object>(Collections.emptySet(), Link.of("/reference/some-id"));
|
||||
CollectionModel<Object> request = CollectionModel.empty(Link.of("/reference/some-id"));
|
||||
|
||||
controller.createPropertyReference(information, HttpMethod.POST, request, 4711, "references");
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.data.rest.webmvc.support.ExceptionMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.mock.http.MockHttpInputMessage;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RepositoryRestExceptionHandler}.
|
||||
@@ -60,7 +61,7 @@ public class RepositoryRestExceptionHandlerUnitTests {
|
||||
public void handlesHttpMessageNotReadableException() {
|
||||
|
||||
ResponseEntity<ExceptionMessage> result = HANDLER
|
||||
.handleNotReadable(new HttpMessageNotReadableException("Message!"));
|
||||
.handleNotReadable(new HttpMessageNotReadableException("Message!", new MockHttpInputMessage(new byte[0])));
|
||||
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.core.Path;
|
||||
@@ -41,7 +42,6 @@ import org.springframework.data.rest.webmvc.support.DefaultedPageable;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
@@ -308,7 +308,7 @@ public class RepositoryRestHandlerMappingUnitTests {
|
||||
ProxyFactory factory = new ProxyFactory(source);
|
||||
Object proxy = factory.getProxy();
|
||||
|
||||
assertThat(ClassUtils.isCglibProxy(proxy)).isTrue();
|
||||
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
|
||||
|
||||
return proxy.getClass();
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Immutable;
|
||||
@@ -59,6 +58,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
@@ -172,7 +172,7 @@ public class DomainObjectReaderUnitTests {
|
||||
assertThat(((Map<Object, Object>) object).get("c")).isEqualTo("2");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAREST-701
|
||||
@Test(expected = JsonMappingException.class) // DATAREST-701
|
||||
public void rejectsMergingUnknownDomainObject() throws Exception {
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
@@ -486,6 +486,7 @@ public class DomainObjectReaderUnitTests {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode node = mapper.readTree("{ \"enums\" : [ \"SECOND\", \"FIRST\" ] }");
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
CollectionOfEnumWithMethods result = reader.merge((ObjectNode) node, sample, mapper);
|
||||
|
||||
assertThat(result.enums).containsExactly(SampleEnum.SECOND, SampleEnum.FIRST);
|
||||
@@ -568,18 +569,19 @@ public class DomainObjectReaderUnitTests {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode source = (ObjectNode) mapper.readTree("{ \"lastLogin\" : null, \"email\" : \"bar@foo.com\"}");
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
SampleUser result = reader.merge(source, user, mapper);
|
||||
|
||||
assertThat(result.lastLogin).isNotNull();
|
||||
assertThat(result.email).isEqualTo("foo@bar.com");
|
||||
}
|
||||
|
||||
|
||||
@Test // DATAREST-1068
|
||||
public void arraysCanBeResizedDuringMerge() throws Exception {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ArrayHolder target = new ArrayHolder(new String[] { });
|
||||
ArrayHolder target = new ArrayHolder(new String[] {});
|
||||
JsonNode node = mapper.readTree("{ \"array\" : [ \"new\" ] }");
|
||||
|
||||
|
||||
ArrayHolder updated = reader.doMerge((ObjectNode) node, target, mapper);
|
||||
assertThat(updated.array).containsExactly("new");
|
||||
}
|
||||
@@ -807,7 +809,7 @@ public class DomainObjectReaderUnitTests {
|
||||
static class WithNullCollection {
|
||||
List<String> strings;
|
||||
}
|
||||
|
||||
|
||||
// DATAREST-1068
|
||||
@Value
|
||||
static class ArrayHolder {
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ProjectionJacksonIntegrationTests {
|
||||
customer.address = new Address();
|
||||
|
||||
CustomerProjection projection = factory.createProjection(CustomerProjection.class, customer);
|
||||
CollectionModel<CustomerProjection> resources = new CollectionModel<CustomerProjection>(Arrays.asList(projection));
|
||||
CollectionModel<CustomerProjection> resources = CollectionModel.of(Arrays.asList(projection));
|
||||
|
||||
String result = mapper.writeValueAsString(resources);
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ public class AssociationsUnitTests {
|
||||
assertThat(links).contains(Link.of("/relatedAndExported{?" + projectionParameterName + "}", "relatedAndExported"));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private Association<? extends PersistentProperty<?>> getAssociation(Class<?> type, String name) {
|
||||
|
||||
KeyValuePersistentEntity<?, ? extends KeyValuePersistentProperty<?>> rootEntity = mappingContext
|
||||
|
||||
Reference in New Issue
Block a user