Skip to content

Commit

Permalink
fix: json patch throw error when remove null property [DHIS2-16299] (…
Browse files Browse the repository at this point in the history
…39) (#15923)

* fix: json patch throw error when remove null property

* fix backport conflict
  • Loading branch information
vietnguyen authored Dec 13, 2023
1 parent bef33eb commit 9653394
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.hisp.dhis.commons.collection.CollectionUtils;
import org.hisp.dhis.commons.jackson.jsonpatch.JsonPatch;
import org.hisp.dhis.commons.jackson.jsonpatch.JsonPatchException;
import org.hisp.dhis.commons.jackson.jsonpatch.JsonPatchOperation;
import org.hisp.dhis.schema.Property;
import org.hisp.dhis.schema.Schema;
import org.hisp.dhis.schema.SchemaService;
Expand Down Expand Up @@ -87,6 +88,8 @@ public <T> T apply(JsonPatch patch, T object) throws JsonPatchException {
// correctly made into json nodes.
handleCollectionUpdates(object, schema, (ObjectNode) node);

validatePatchPath(patch, schema);

node = patch.apply(node);
return (T)
jsonToObject(
Expand Down Expand Up @@ -137,4 +140,17 @@ private BaseIdentifiableObject shallowCopyIdentifiableObject(BaseIdentifiableObj
clone.setUid(source.getUid());
return clone;
}

/** Check if all patch paths are valid for the given schema. */
private void validatePatchPath(JsonPatch patch, Schema schema) throws JsonPatchException {
for (JsonPatchOperation op : patch.getOperations()) {
if (!schema.haveProperty(op.getPath().getMatchingProperty())) {
throw new JsonPatchException(
"Property "
+ op.getPath().getMatchingProperty()
+ " does not exist on "
+ schema.getClass().getSimpleName());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public JsonNode apply(JsonNode node) throws JsonPatchException {
}

if (!nodePathExists(node)) {
throw new JsonPatchException(String.format("Invalid path %s", path));
return node;
}

final JsonNode parentNode = node.at(path.head());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
Expand All @@ -52,17 +50,6 @@
class RemoveOperationTest {
private final ObjectMapper jsonMapper = JacksonObjectMapperConfig.staticJsonMapper();

@Test
void testRemoveInvalidKeyShouldThrowException() throws JsonProcessingException {
JsonPatch patch =
jsonMapper.readValue(
"[" + "{\"op\": \"remove\", \"path\": \"/aaa\"}" + "]", JsonPatch.class);
assertNotNull(patch);
JsonNode root = jsonMapper.createObjectNode();
assertFalse(root.has("aaa"));
assertThrows(JsonPatchException.class, () -> patch.apply(root));
}

@Test
void testRemoveProperty() throws JsonProcessingException, JsonPatchException {
JsonPatch patch =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.fasterxml.jackson.core.JsonProcessingException;
Expand Down Expand Up @@ -65,21 +64,6 @@ void testBasicPropertyReplacement() throws JsonProcessingException, JsonPatchExc
assertEquals("bbb", root.get("aaa").asText());
}

@Test
void testBasicReplaceNotExistProperty() throws JsonProcessingException {

JsonPatch patch =
jsonMapper.readValue(
"[" + "{\"op\": \"replace\", \"path\": \"/notExist\", \"value\": \"bbb\"}" + "]",
JsonPatch.class);
assertNotNull(patch);
ObjectNode root = jsonMapper.createObjectNode();
root.set("aaa", TextNode.valueOf("aaa"));
assertTrue(root.has("aaa"));
assertEquals("aaa", root.get("aaa").asText());
assertThrows(JsonPatchException.class, () -> patch.apply(root));
}

@Test
void testBasicTextToArray() throws JsonProcessingException, JsonPatchException {
JsonPatch patch =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -42,6 +43,7 @@
import org.hisp.dhis.dataelement.DataElementGroup;
import org.hisp.dhis.test.integration.IntegrationTestBase;
import org.hisp.dhis.user.User;
import org.hisp.dhis.user.UserRole;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

Expand Down Expand Up @@ -198,4 +200,70 @@ void testAddAndRemoveSharingUser() throws JsonProcessingException, JsonPatchExce
DataElement removedPatchedDE = jsonPatchManager.apply(removePatch, patchedDE);
assertEquals(0, removedPatchedDE.getSharing().getUsers().size());
}

@Test
void testReplaceNullProperty() throws JsonProcessingException, JsonPatchException {
UserRole userRole = createUserRole("test", "ALL");
userRole.setCode(null);
manager.save(userRole);
JsonPatch patch =
jsonMapper.readValue(
"[" + "{\"op\": \"replace\", \"path\": \"/code\", \"value\": \"updated\"}" + "]",
JsonPatch.class);
assertNotNull(patch);
UserRole patchedUserRole = jsonPatchManager.apply(patch, userRole);
assertEquals("updated", patchedUserRole.getCode());
}

@Test
void testReplaceNotExistProperty() throws JsonProcessingException {
UserRole userRole = createUserRole("test", "ALL");
userRole.setCode(null);
manager.save(userRole);
JsonPatch patch =
jsonMapper.readValue(
"[" + "{\"op\": \"replace\", \"path\": \"/notexist\", \"value\": \"updated\"}" + "]",
JsonPatch.class);
assertNotNull(patch);
assertThrows(JsonPatchException.class, () -> jsonPatchManager.apply(patch, userRole));
}

@Test
void testAddNotExistProperty() throws JsonProcessingException {
UserRole userRole = createUserRole("test", "ALL");
userRole.setCode(null);
manager.save(userRole);
JsonPatch patch =
jsonMapper.readValue(
"[" + "{\"op\": \"add\", \"path\": \"/notexist\", \"value\": \"updated\"}" + "]",
JsonPatch.class);
assertNotNull(patch);
assertThrows(JsonPatchException.class, () -> jsonPatchManager.apply(patch, userRole));
}

@Test
void testRemoveNotExistProperty() throws JsonProcessingException {
UserRole userRole = createUserRole("test", "ALL");
userRole.setCode(null);
manager.save(userRole);
JsonPatch patch =
jsonMapper.readValue(
"[" + "{\"op\": \"remove\", \"path\": \"/notexist\", \"value\": \"updated\"}" + "]",
JsonPatch.class);
assertNotNull(patch);
assertThrows(JsonPatchException.class, () -> jsonPatchManager.apply(patch, userRole));
}

@Test
void testRemoveByIdNotExistProperty() throws JsonProcessingException {
UserRole userRole = createUserRole("test", "ALL");
userRole.setCode(null);
manager.save(userRole);
JsonPatch patch =
jsonMapper.readValue(
"[" + "{\"op\": \"remove\", \"path\": \"/notexist\", \"id\": \"uid\"}" + "]",
JsonPatch.class);
assertNotNull(patch);
assertThrows(JsonPatchException.class, () -> jsonPatchManager.apply(patch, userRole));
}
}

0 comments on commit 9653394

Please sign in to comment.