DATAREST-1383 - PATCH requests now skip application of values for backend read-only properties.
MappedProperties now immediately drops non-writable properties when created for deserialization. Previously those properties would have to be annotated with @JsonProperty(access = Access.READ_ONLY) explicitly to avoid them being considered.
This commit is contained in:
@@ -227,6 +227,7 @@ public class DomainObjectReader {
|
||||
String fieldName = entry.getKey();
|
||||
|
||||
if (!mappedProperties.hasPersistentPropertyForField(fieldName)) {
|
||||
i.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
@@ -61,7 +62,8 @@ class MappedProperties {
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param description must not be {@literal null}.
|
||||
*/
|
||||
private MappedProperties(PersistentEntity<?, ? extends PersistentProperty<?>> entity, BeanDescription description) {
|
||||
private MappedProperties(PersistentEntity<?, ? extends PersistentProperty<?>> entity, BeanDescription description,
|
||||
Predicate<PersistentProperty<?>> filter) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null!");
|
||||
Assert.notNull(description, "BeanDescription must not be null!");
|
||||
@@ -79,10 +81,12 @@ class MappedProperties {
|
||||
Optional<? extends PersistentProperty<?>> persistentProperty = //
|
||||
Optional.ofNullable(entity.getPersistentProperty(property.getInternalName()));
|
||||
|
||||
persistentProperty.ifPresent(it -> {
|
||||
propertyToFieldName.put(it, property);
|
||||
fieldNameToProperty.put(property.getName(), it);
|
||||
});
|
||||
persistentProperty//
|
||||
.filter(filter) //
|
||||
.ifPresent(it -> {
|
||||
propertyToFieldName.put(it, property);
|
||||
fieldNameToProperty.put(property.getName(), it);
|
||||
});
|
||||
|
||||
if (!persistentProperty.isPresent()) {
|
||||
unmappedProperties.add(property);
|
||||
@@ -104,7 +108,7 @@ class MappedProperties {
|
||||
BeanDescription description = INTROSPECTOR.forDeserialization(config, mapper.constructType(entity.getType()),
|
||||
config);
|
||||
|
||||
return new MappedProperties(entity, description);
|
||||
return new MappedProperties(entity, description, it -> it.isWritable());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +124,7 @@ class MappedProperties {
|
||||
SerializationConfig config = mapper.getSerializationConfig();
|
||||
BeanDescription description = INTROSPECTOR.forSerialization(config, mapper.constructType(entity.getType()), config);
|
||||
|
||||
return new MappedProperties(entity, description);
|
||||
return new MappedProperties(entity, description, it -> true);
|
||||
}
|
||||
|
||||
public static MappedProperties none() {
|
||||
|
||||
@@ -569,6 +569,22 @@ public class DomainObjectReaderUnitTests {
|
||||
assertThat(result.strings).containsExactly("value");
|
||||
}
|
||||
|
||||
@Test // DATAREST-1383
|
||||
public void doesNotWipeReadOnlyPropertyForPatch() throws Exception {
|
||||
|
||||
SampleUser user = new SampleUser("name", "password");
|
||||
user.lastLogin = new Date();
|
||||
user.email = "foo@bar.com";
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode source = (ObjectNode) mapper.readTree("{ \"lastLogin\" : null, \"email\" : \"bar@foo.com\"}");
|
||||
|
||||
SampleUser result = reader.merge(source, user, mapper);
|
||||
|
||||
assertThat(result.lastLogin).isNotNull();
|
||||
assertThat(result.email).isEqualTo("foo@bar.com");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T as(Object source, Class<T> type) {
|
||||
|
||||
@@ -586,6 +602,9 @@ public class DomainObjectReaderUnitTests {
|
||||
@JsonProperty(access = READ_ONLY) //
|
||||
private Date lastLogin;
|
||||
|
||||
@ReadOnlyProperty //
|
||||
private String email;
|
||||
|
||||
public SampleUser(String name, String password) {
|
||||
|
||||
this.name = name;
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.rest.webmvc.json;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
@@ -84,6 +85,20 @@ public class MappedPropertiesUnitTests {
|
||||
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNotNull();
|
||||
}
|
||||
|
||||
@Test // DATAREST-1383
|
||||
public void doesNotRegardReadOnlyPropertyForDeserialization() {
|
||||
|
||||
MappedProperties properties = MappedProperties.forDeserialization(entity, mapper);
|
||||
|
||||
assertThat(properties.hasPersistentPropertyForField("anotherReadOnlyProperty")).isFalse();
|
||||
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNull();
|
||||
|
||||
properties = MappedProperties.forSerialization(entity, mapper);
|
||||
|
||||
assertThat(properties.hasPersistentPropertyForField("anotherReadOnlyProperty")).isTrue();
|
||||
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNotNull();
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
|
||||
public @Transient String notExposedBySpringData;
|
||||
@@ -91,5 +106,6 @@ public class MappedPropertiesUnitTests {
|
||||
public String exposedProperty;
|
||||
public @JsonProperty("email") String emailAddress;
|
||||
public @JsonProperty(access = Access.READ_ONLY) String readOnlyProperty;
|
||||
public @ReadOnlyProperty String anotherReadOnlyProperty;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user