DATAREST-1030 - Reinstantiate PATCH behaviour for associations.
We now completely opt out of merging values if the property is a linkable association.
This commit is contained in:
@@ -232,7 +232,7 @@ public class DomainObjectReader {
|
||||
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(target);
|
||||
Optional<Object> rawValue = accessor.getProperty(property);
|
||||
|
||||
if (!rawValue.isPresent()) {
|
||||
if (!rawValue.isPresent() || associationLinks.isLinkableAssociation(property)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -249,10 +249,6 @@ public class DomainObjectReader {
|
||||
|
||||
if (child.isObject()) {
|
||||
|
||||
if (associationLinks.isLinkableAssociation(property)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectNode objectNode = (ObjectNode) child;
|
||||
|
||||
if (property.isMap()) {
|
||||
|
||||
@@ -23,9 +23,11 @@ import static org.mockito.Mockito.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
@@ -38,10 +40,12 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
@@ -51,6 +55,7 @@ import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
@@ -59,9 +64,14 @@ import org.springframework.data.rest.webmvc.mapping.Associations;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
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.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
@@ -79,6 +89,7 @@ public class DomainObjectReaderUnitTests {
|
||||
@Mock ResourceMappings mappings;
|
||||
|
||||
DomainObjectReader reader;
|
||||
PersistentEntities entities;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -98,10 +109,10 @@ public class DomainObjectReaderUnitTests {
|
||||
mappingContext.getPersistentEntity(TransientReadOnlyProperty.class);
|
||||
mappingContext.getPersistentEntity(CollectionOfEnumWithMethods.class);
|
||||
mappingContext.getPersistentEntity(SampleWithReference.class);
|
||||
mappingContext.getPersistentEntity(Note.class);
|
||||
mappingContext.afterPropertiesSet();
|
||||
|
||||
PersistentEntities entities = new PersistentEntities(Collections.singleton(mappingContext));
|
||||
|
||||
this.entities = new PersistentEntities(Collections.singleton(mappingContext));
|
||||
this.reader = new DomainObjectReader(entities, new Associations(mappings, mock(RepositoryRestConfiguration.class)));
|
||||
}
|
||||
|
||||
@@ -497,6 +508,35 @@ public class DomainObjectReaderUnitTests {
|
||||
assertThat(result.nested).isSameAs(originalCollection);
|
||||
}
|
||||
|
||||
@Test // DATAREST-1030
|
||||
public void patchWithReferenceToRelatedEntityIsResolvedCorrectly() throws Exception {
|
||||
|
||||
Associations associations = mock(Associations.class);
|
||||
PersistentProperty<?> any = ArgumentMatchers.any(PersistentProperty.class);
|
||||
when(associations.isLinkableAssociation(any)).thenReturn(true);
|
||||
|
||||
DomainObjectReader reader = new DomainObjectReader(entities, associations);
|
||||
|
||||
Tag first = new Tag();
|
||||
Tag second = new Tag();
|
||||
|
||||
Note note = new Note();
|
||||
note.tags.add(first);
|
||||
note.tags.add(second);
|
||||
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addDeserializer(Tag.class, new SelectValueByIdSerializer<Tag>(Collections.singletonMap(second.id, second)));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(module);
|
||||
|
||||
ObjectNode readTree = (ObjectNode) mapper.readTree(String.format("{ \"tags\" : [ \"%s\"]}", second.id));
|
||||
|
||||
Note result = reader.doMerge(readTree, note, mapper);
|
||||
|
||||
assertThat(result.tags).contains(second);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T as(Object source, Class<T> type) {
|
||||
|
||||
@@ -670,4 +710,39 @@ public class DomainObjectReaderUnitTests {
|
||||
static class Nested {
|
||||
int x, y;
|
||||
}
|
||||
|
||||
// DATAREST-1030
|
||||
|
||||
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
|
||||
static class Note {
|
||||
@Id UUID id = UUID.randomUUID();
|
||||
@Reference List<Tag> tags = new ArrayList<Tag>();
|
||||
}
|
||||
|
||||
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
|
||||
static class Tag {
|
||||
@Id UUID id = UUID.randomUUID();
|
||||
String name;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class SelectValueByIdSerializer<T> extends JsonDeserializer<T> {
|
||||
|
||||
private final Map<? extends Object, T> values;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
|
||||
*/
|
||||
@Override
|
||||
public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
|
||||
String text = p.getText();
|
||||
|
||||
return values.entrySet().stream()//
|
||||
.filter(it -> it.getKey().toString().equals(text))//
|
||||
.map(it -> it.getValue())//
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user