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:
Oliver Drotbohm
2020-03-27 14:10:41 +01:00
parent 774fed5524
commit 4c17d54e9a
28 changed files with 163 additions and 139 deletions

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);

View File

@@ -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

View File

@@ -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() {

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);
}
/**

View File

@@ -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 -> {

View File

@@ -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);
}
/**

View File

@@ -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));
}
}
}

View File

@@ -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.
*

View File

@@ -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()));

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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))//

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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