Skip to content

Commit 2878711

Browse files
committed
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.
1 parent 339c3d2 commit 2878711

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/DomainObjectReader.java

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ <T> T doMerge(ObjectNode root, T target, ObjectMapper mapper) throws Exception {
227227
String fieldName = entry.getKey();
228228

229229
if (!mappedProperties.hasPersistentPropertyForField(fieldName)) {
230+
i.remove();
230231
continue;
231232
}
232233

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/MappedProperties.java

+11-7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Map;
2727
import java.util.Optional;
2828
import java.util.Set;
29+
import java.util.function.Predicate;
2930

3031
import org.springframework.data.mapping.PersistentEntity;
3132
import org.springframework.data.mapping.PersistentProperty;
@@ -61,7 +62,8 @@ class MappedProperties {
6162
* @param entity must not be {@literal null}.
6263
* @param description must not be {@literal null}.
6364
*/
64-
private MappedProperties(PersistentEntity<?, ? extends PersistentProperty<?>> entity, BeanDescription description) {
65+
private MappedProperties(PersistentEntity<?, ? extends PersistentProperty<?>> entity, BeanDescription description,
66+
Predicate<PersistentProperty<?>> filter) {
6567

6668
Assert.notNull(entity, "Entity must not be null!");
6769
Assert.notNull(description, "BeanDescription must not be null!");
@@ -79,10 +81,12 @@ private MappedProperties(PersistentEntity<?, ? extends PersistentProperty<?>> en
7981
Optional<? extends PersistentProperty<?>> persistentProperty = //
8082
Optional.ofNullable(entity.getPersistentProperty(property.getInternalName()));
8183

82-
persistentProperty.ifPresent(it -> {
83-
propertyToFieldName.put(it, property);
84-
fieldNameToProperty.put(property.getName(), it);
85-
});
84+
persistentProperty//
85+
.filter(filter) //
86+
.ifPresent(it -> {
87+
propertyToFieldName.put(it, property);
88+
fieldNameToProperty.put(property.getName(), it);
89+
});
8690

8791
if (!persistentProperty.isPresent()) {
8892
unmappedProperties.add(property);
@@ -104,7 +108,7 @@ public static MappedProperties forDeserialization(PersistentEntity<?, ?> entity,
104108
BeanDescription description = INTROSPECTOR.forDeserialization(config, mapper.constructType(entity.getType()),
105109
config);
106110

107-
return new MappedProperties(entity, description);
111+
return new MappedProperties(entity, description, it -> it.isWritable());
108112
}
109113

110114
/**
@@ -120,7 +124,7 @@ public static MappedProperties forSerialization(PersistentEntity<?, ?> entity, O
120124
SerializationConfig config = mapper.getSerializationConfig();
121125
BeanDescription description = INTROSPECTOR.forSerialization(config, mapper.constructType(entity.getType()), config);
122126

123-
return new MappedProperties(entity, description);
127+
return new MappedProperties(entity, description, it -> true);
124128
}
125129

126130
public static MappedProperties none() {

spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/DomainObjectReaderUnitTests.java

+19
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,22 @@ public void mergesIntoUninitializedCollection() throws Exception {
569569
assertThat(result.strings).containsExactly("value");
570570
}
571571

572+
@Test // DATAREST-1383
573+
public void doesNotWipeReadOnlyPropertyForPatch() throws Exception {
574+
575+
SampleUser user = new SampleUser("name", "password");
576+
user.lastLogin = new Date();
577+
user.email = "[email protected]";
578+
579+
ObjectMapper mapper = new ObjectMapper();
580+
ObjectNode source = (ObjectNode) mapper.readTree("{ \"lastLogin\" : null, \"email\" : \"[email protected]\"}");
581+
582+
SampleUser result = reader.merge(source, user, mapper);
583+
584+
assertThat(result.lastLogin).isNotNull();
585+
assertThat(result.email).isEqualTo("[email protected]");
586+
}
587+
572588
@SuppressWarnings("unchecked")
573589
private static <T> T as(Object source, Class<T> type) {
574590

@@ -586,6 +602,9 @@ static class SampleUser {
586602
@JsonProperty(access = READ_ONLY) //
587603
private Date lastLogin;
588604

605+
@ReadOnlyProperty //
606+
private String email;
607+
589608
public SampleUser(String name, String password) {
590609

591610
this.name = name;

spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/MappedPropertiesUnitTests.java

+16
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.assertj.core.api.Assertions.*;
1919

2020
import org.junit.Test;
21+
import org.springframework.data.annotation.ReadOnlyProperty;
2122
import org.springframework.data.annotation.Transient;
2223
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
2324
import org.springframework.data.mapping.PersistentEntity;
@@ -84,12 +85,27 @@ public void doesNotExcludeReadOnlyPropertiesForSerialization() {
8485
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNotNull();
8586
}
8687

88+
@Test // DATAREST-1383
89+
public void doesNotRegardReadOnlyPropertyForDeserialization() {
90+
91+
MappedProperties properties = MappedProperties.forDeserialization(entity, mapper);
92+
93+
assertThat(properties.hasPersistentPropertyForField("anotherReadOnlyProperty")).isFalse();
94+
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNull();
95+
96+
properties = MappedProperties.forSerialization(entity, mapper);
97+
98+
assertThat(properties.hasPersistentPropertyForField("anotherReadOnlyProperty")).isTrue();
99+
assertThat(properties.getPersistentProperty("readOnlyProperty")).isNotNull();
100+
}
101+
87102
static class Sample {
88103

89104
public @Transient String notExposedBySpringData;
90105
public @JsonIgnore String notExposedByJackson;
91106
public String exposedProperty;
92107
public @JsonProperty("email") String emailAddress;
93108
public @JsonProperty(access = Access.READ_ONLY) String readOnlyProperty;
109+
public @ReadOnlyProperty String anotherReadOnlyProperty;
94110
}
95111
}

0 commit comments

Comments
 (0)