DATAREST-1338 - JsonPatch handling is now able to traverse maps.

We're now able to correctly identify Map keys within JsonPatch paths so that e.g. people/Dave/name properly translates into a people['Dave'].name SpEL expression.

Streamlined the design of SpelPath to avoid duplicate parsing and properly express the difference between typed and untyped paths in the type hierarchy. Slightly refactored the type hierarchy so that only methods that make sense in either untyped or typed state appear on the instance returned (getLeafType(Class) was exposed on TypedSpelPath before, which was confusing).
This commit is contained in:
Oliver Drotbohm
2019-01-29 14:06:49 +01:00
parent e6c816faad
commit 017b69c4e1
11 changed files with 329 additions and 130 deletions

View File

@@ -15,13 +15,17 @@
*/
package org.springframework.data.rest.webmvc.json.patch;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ReplaceOperationTests {
@@ -82,4 +86,30 @@ public class ReplaceOperationTests {
assertNotNull(todo.getType().getValue());
assertTrue(todo.getType().getValue().equals("new"));
}
@Test // DATAREST-1338
public void replacesMapValueCorrectly() throws Exception {
Book book = new Book();
book.characters = new HashMap<>();
book.characters.put("protagonist", "Pinco");
ReplaceOperation.valueAt("/characters/protagonist") //
.with(prepareValue("\"Pallo\"")) //
.perform(book, Book.class);
assertThat(book.characters.get("protagonist")).isEqualTo("Pallo");
}
private static Object prepareValue(String json) throws Exception {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
return new JsonLateObjectEvaluator(mapper, node);
}
// DATAREST-1338
class Book {
public Map<String, String> characters;
}
}

View File

@@ -20,9 +20,11 @@ import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.data.rest.webmvc.json.patch.SpelPath.TypedSpelPath;
import org.springframework.data.rest.webmvc.json.patch.SpelPath.UntypedSpelPath;
/**
* Unit tests for {@link SpelPath}.
@@ -34,34 +36,34 @@ public class SpelPathUnitTests {
@Test
public void listIndex() {
SpelPath expr = SpelPath.of("/1/description");
UntypedSpelPath expr = SpelPath.untyped("/1/description");
List<Todo> todos = new ArrayList<Todo>();
todos.add(new Todo(1L, "A", false));
todos.add(new Todo(2L, "B", false));
todos.add(new Todo(3L, "C", false));
assertEquals("B", (String) expr.bindTo(Todo.class).getValue(todos));
assertEquals("B", expr.bindTo(Todo.class).getValue(todos));
}
@Test
public void accessesLastCollectionElementWithDash() {
SpelPath expr = SpelPath.of("/-/description");
UntypedSpelPath expr = SpelPath.untyped("/-/description");
List<Todo> todos = new ArrayList<Todo>();
todos.add(new Todo(1L, "A", false));
todos.add(new Todo(2L, "B", false));
todos.add(new Todo(3L, "C", false));
assertEquals("C", (String) expr.bindTo(Todo.class).getValue(todos));
assertEquals("C", expr.bindTo(Todo.class).getValue(todos));
}
@Test // DATAREST-1152
public void cachesSpelPath() {
SpelPath left = SpelPath.of("/description");
SpelPath right = SpelPath.of("/description");
UntypedSpelPath left = SpelPath.untyped("/description");
UntypedSpelPath right = SpelPath.untyped("/description");
assertSame(left, right);
}
@@ -69,7 +71,7 @@ public class SpelPathUnitTests {
@Test // DATAREST-1152
public void cachesTypedSpelPath() {
SpelPath source = SpelPath.of("/description");
UntypedSpelPath source = SpelPath.untyped("/description");
TypedSpelPath left = source.bindTo(Todo.class);
TypedSpelPath right = source.bindTo(Todo.class);
@@ -78,6 +80,35 @@ public class SpelPathUnitTests {
@Test // DATAREST-1274
public void supportsMultiDigitCollectionIndex() {
assertThat(SpelPath.of("/11/description").getLeafType(Todo.class)).isEqualTo(String.class);
assertThat(SpelPath.untyped("/11/description").bindTo(Todo.class).getLeafType()).isEqualTo(String.class);
}
@Test // DATAREST-1338
public void handlesStringMapKeysInPathExpressions() {
TypedSpelPath path = SpelPath.untyped("people/Dave/name").bindTo(MapWrapper.class);
assertThat(path.getExpressionString()).isEqualTo("people['Dave'].name");
assertThat(path.getLeafType()).isEqualTo(String.class);
}
@Test // DATAREST-1338
public void handlesIntegerMapKeysInPathExpressions() {
TypedSpelPath path = SpelPath.untyped("peopleByInt/0/name").bindTo(MapWrapper.class);
assertThat(path.getExpressionString()).isEqualTo("peopleByInt[0].name");
assertThat(path.getLeafType()).isEqualTo(String.class);
}
// DATAREST-1338
static class Person {
String name;
}
static class MapWrapper {
Map<String, Person> people;
Map<Integer, Person> peopleByInt;
}
}