diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java index ef0f0dc73..b2f6874d1 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java @@ -227,6 +227,7 @@ public class DomainObjectReader { String fieldName = entry.getKey(); if (!mappedProperties.hasPersistentPropertyForField(fieldName)) { + i.remove(); continue; } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java index 319afbb46..563f7bffd 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java @@ -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> entity, BeanDescription description) { + private MappedProperties(PersistentEntity> entity, BeanDescription description, + Predicate> filter) { Assert.notNull(entity, "Entity must not be null!"); Assert.notNull(description, "BeanDescription must not be null!"); @@ -79,10 +81,12 @@ class MappedProperties { Optional> 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() { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java index a8168fac6..fdd5d523d 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java @@ -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 as(Object source, Class 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; diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java index fae253f96..bc0917fc6 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java @@ -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; } }