DATAREST-1248 - SortTranslator now considers read-only properties sortable.

MappedProperties now exposes a factory method that uses the Jackson introspector for serialization (instead of deserialization) which now also includes read-only properties. Previously, read-only ones were not considered as they are excluded from the metadata if it is looked up for deserialization.
This commit is contained in:
Oliver Gierke
2018-08-07 15:43:50 +02:00
parent 994eed6e27
commit 2c8fb45881
6 changed files with 53 additions and 10 deletions

View File

@@ -216,7 +216,7 @@ public class DomainObjectReader {
}
PersistentEntity<?, ?> entity = candidate.get();
MappedProperties mappedProperties = MappedProperties.fromJacksonProperties(entity, mapper);
MappedProperties mappedProperties = MappedProperties.forDeserialization(entity, mapper);
for (Iterator<Entry<String, JsonNode>> i = root.fields(); i.hasNext();) {
@@ -616,7 +616,7 @@ public class DomainObjectReader {
Assert.notNull(entity, "PersistentEntity must not be null!");
Assert.notNull(mapper, "ObjectMapper must not be null!");
this.properties = MappedProperties.fromJacksonProperties(entity, mapper);
this.properties = MappedProperties.forDeserialization(entity, mapper);
this.targetAccessor = new ConvertingPropertyAccessor(entity.getPropertyAccessor(target),
new DefaultConversionService());
this.sourceAccessor = entity.getPropertyAccessor(source);

View File

@@ -226,7 +226,7 @@ public class JacksonMappingAwareSortTranslator {
this.objectMapper = objectMapper;
this.currentType = persistentEntity;
this.currentProperties = persistentEntity//
.map(it -> MappedProperties.fromJacksonProperties(it, objectMapper))//
.map(it -> MappedProperties.forSerialization(it, objectMapper))//
.orElseGet(() -> MappedProperties.none());
this.currentWrappedProperties = persistentEntity//
.map(it -> WrappedProperties.fromJacksonProperties(persistentEntities, it, objectMapper))//

View File

@@ -32,7 +32,9 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.util.Assert;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
@@ -89,16 +91,34 @@ class MappedProperties {
}
/**
* Creates {@link MappedProperties} for the given {@link PersistentEntity}.
* Creates {@link MappedProperties} for the given {@link PersistentEntity} for deserialization purposes. Will not
* include Jackson-read-only properties.
*
* @param entity must not be {@literal null}.
* @param mapper must not be {@literal null}.
* @return
*/
public static MappedProperties fromJacksonProperties(PersistentEntity<?, ?> entity, ObjectMapper mapper) {
public static MappedProperties forDeserialization(PersistentEntity<?, ?> entity, ObjectMapper mapper) {
BeanDescription description = INTROSPECTOR.forDeserialization(mapper.getDeserializationConfig(),
mapper.constructType(entity.getType()), mapper.getDeserializationConfig());
DeserializationConfig config = mapper.getDeserializationConfig();
BeanDescription description = INTROSPECTOR.forDeserialization(config, mapper.constructType(entity.getType()),
config);
return new MappedProperties(entity, description);
}
/**
* Creates {@link MappedProperties} for the given {@link PersistentEntity} for serialization purposes. Includes
* Jackson-read-only properties.
*
* @param entity must not be {@literal null}.
* @param mapper must not be {@literal null}.
* @return
*/
public static MappedProperties forSerialization(PersistentEntity<?, ?> entity, ObjectMapper mapper) {
SerializationConfig config = mapper.getSerializationConfig();
BeanDescription description = INTROSPECTOR.forSerialization(config, mapper.constructType(entity.getType()), config);
return new MappedProperties(entity, description);
}

View File

@@ -36,6 +36,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
@@ -202,8 +203,10 @@ class WrappedProperties {
}
private BeanDescription getBeanDescription(Class<?> type) {
return INTROSPECTOR.forDeserialization(mapper.getDeserializationConfig(), mapper.constructType(type),
mapper.getDeserializationConfig());
SerializationConfig config = mapper.getSerializationConfig();
return INTROSPECTOR.forSerialization(config, mapper.constructType(type), config);
}
private static Optional<AnnotatedMember> findAnnotatedMember(BeanPropertyDefinition property) {

View File

@@ -37,7 +37,7 @@ public class MappedPropertiesUnitTests {
ObjectMapper mapper = new ObjectMapper();
KeyValueMappingContext<?, ?> context = new KeyValueMappingContext<>();
PersistentEntity<?, ?> entity = context.getRequiredPersistentEntity(Sample.class);
MappedProperties properties = MappedProperties.fromJacksonProperties(entity, mapper);
MappedProperties properties = MappedProperties.forDeserialization(entity, mapper);
@Test // DATAREST-575
public void doesNotExposeMappedPropertyForNonSpringDataPersistentProperty() {
@@ -75,6 +75,15 @@ public class MappedPropertiesUnitTests {
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNull();
}
@Test // DATAREST-1248
public void doesNotExcludeReadOnlyPropertiesForSerialization() {
MappedProperties properties = MappedProperties.forSerialization(entity, mapper);
assertThat(properties.hasPersistentPropertyForField("readOnlyProperty")).isTrue();
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNotNull();
}
static class Sample {
public @Transient String notExposedBySpringData;

View File

@@ -34,6 +34,7 @@ import org.springframework.data.rest.webmvc.json.JacksonMappingAwareSortTranslat
import org.springframework.data.rest.webmvc.mapping.Associations;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -171,12 +172,22 @@ public class SortTranslatorUnitTests {
assertThat(translatedSort.getOrderFor("burrito.embedded.name")).isNotNull();
}
@Test // DATAREST-1248
public void allowsSortingByReadOnlyProperty() {
Sort sort = sortTranslator.translateSort(Sort.by("readOnly"),
mappingContext.getRequiredPersistentEntity(Plain.class));
assertThat(sort.getOrderFor("readOnly")).isNotNull();
}
static class Plain {
public String name;
public Embedded embedded;
@Reference public Embedded refEmbedded;
@Reference public AnotherRootEntity association;
@JsonProperty(access = Access.READ_ONLY) public String readOnly;
}
static class UnwrapEmbedded {