Search in sources :

Example 1 with AddOperation

use of org.springframework.sync.AddOperation in project spring-sync by spring-projects.

the class JsonPatchPatchConverter method convert.

/**
	 * Constructs a {@link Patch} object given a JsonNode.
	 * @param jsonNode a JsonNode containing the JSON Patch
	 * @return a {@link Patch}
	 */
public Patch convert(JsonNode jsonNode) {
    if (!(jsonNode instanceof ArrayNode)) {
        throw new IllegalArgumentException("JsonNode must be an instance of ArrayNode");
    }
    ArrayNode opNodes = (ArrayNode) jsonNode;
    List<PatchOperation> ops = new ArrayList<PatchOperation>(opNodes.size());
    for (Iterator<JsonNode> elements = opNodes.elements(); elements.hasNext(); ) {
        JsonNode opNode = elements.next();
        String opType = opNode.get("op").textValue();
        String path = opNode.get("path").textValue();
        JsonNode valueNode = opNode.get("value");
        Object value = valueFromJsonNode(path, valueNode);
        String from = opNode.has("from") ? opNode.get("from").textValue() : null;
        if (opType.equals("test")) {
            ops.add(new TestOperation(path, value));
        } else if (opType.equals("replace")) {
            ops.add(new ReplaceOperation(path, value));
        } else if (opType.equals("remove")) {
            ops.add(new RemoveOperation(path));
        } else if (opType.equals("add")) {
            ops.add(new AddOperation(path, value));
        } else if (opType.equals("copy")) {
            ops.add(new CopyOperation(path, from));
        } else if (opType.equals("move")) {
            ops.add(new MoveOperation(path, from));
        } else {
            throw new PatchException("Unrecognized operation type: " + opType);
        }
    }
    return new Patch(ops);
}
Also used : ArrayList(java.util.ArrayList) RemoveOperation(org.springframework.sync.RemoveOperation) JsonNode(com.fasterxml.jackson.databind.JsonNode) AddOperation(org.springframework.sync.AddOperation) TestOperation(org.springframework.sync.TestOperation) CopyOperation(org.springframework.sync.CopyOperation) ReplaceOperation(org.springframework.sync.ReplaceOperation) MoveOperation(org.springframework.sync.MoveOperation) PatchOperation(org.springframework.sync.PatchOperation) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) PatchException(org.springframework.sync.PatchException) Patch(org.springframework.sync.Patch)

Example 2 with AddOperation

use of org.springframework.sync.AddOperation in project spring-sync by spring-projects.

the class DiffSyncTest method patchList_addNewItem_lostReturn.

//
// Guaranteed Delivery - Lost outbound packet scenario
//
// TODO: This is primarily a client-side case. By definition, the server never receives the patch.
//       Therefore, there's nothing server-side to be tested.
//       However, this case *does* apply to Spring Sync when used in an Android client.
//       Therefore, tests for this scenario will need to be fleshed out.
//
// Guaranteed Delivery - Lost return packet scenario
//
@Test
public void patchList_addNewItem_lostReturn() throws Exception {
    DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
    // Create the list resource
    List<Todo> todos = getTodoList();
    // Apply an initial patch to get the server shadow's client version bumped up.
    // Initially, the server shadow's server and client versions are both 0,
    // matching the incoming patch's versions, so the patch is applied normally.
    List<PatchOperation> ops1 = new ArrayList<PatchOperation>();
    ops1.add(new AddOperation("/~", new Todo(100L, "NEW ITEM 100", false)));
    VersionedPatch versionedPatch = new VersionedPatch(ops1, 0, 0);
    // At this point, the client sends the patch to the server, the client puts the patch in an outbound stack, 
    // the client increments its shadow client version to 1, and the server calls sync.apply() to apply the patch.
    List<Todo> patched = sync.apply(todos, versionedPatch);
    // After the patch is applied, the server shadow versions are
    //   - Primary shadow: serverVersion = 0, clientVersion = 1
    //   - Backup shadow : serverVersion = 0, clientVersion = 1
    // At this point, the server's shadow has client version 1 and server version 0
    // The server then copies its current shadow to backup shadow before performing a new diff against the shadow, bumping the server version to 1 *after* the diff is performed.
    // The backup shadow, having been taken before the new diff was created, still has server version 0.
    // Before it performs the diff, however, it copies its current shadow to backup shadow.
    // The diff was performed against the shadow whose client version 1 and server version 0, therefore the patch will have client version 1 and server version 0.
    VersionedPatch lostDiff = sync.diff(patched);
    // After the diff is applied, the server shadow's server version is incremented.
    //   - Primary shadow: serverVersion = 1, clientVersion = 1
    //   - Backup shadow : serverVersion = 0, clientVersion = 1
    // Verify that the patch has client version 1, server version 0
    assertEquals(1, lostDiff.getClientVersion());
    assertEquals(0, lostDiff.getServerVersion());
    // In the lost return packet scenario, the client never receives that return diff (lostDiff) or acknowledgement of the server having applied the first patch.
    // The client can only assume that the server never received it (although it did).
    // So it produces a new patch against its shadow (whose server version is still at 0 and client version is 1).
    // It then sends both patches to the server and the server attempts to apply them both.
    List<PatchOperation> ops2 = new ArrayList<PatchOperation>();
    ops2.add(new AddOperation("/~", new Todo(200L, "NEW ITEM 200", false)));
    VersionedPatch versionedPatch2 = new VersionedPatch(ops2, 0, 1);
    patched = sync.apply(patched, versionedPatch, versionedPatch2);
    // The first patch's server version is 0, which is less than the server shadow's server version of 1.
    // This indicates a lost packet scenario, meaning that the client never received or applied the
    // return patch from the previous cycle.
    // So the server resurrects the backup shadow into the primary shadow:
    //   - Primary shadow: serverVersion = 0, clientVersion = 1
    //   - Backup shadow : serverVersion = 0, clientVersion = 1
    // Then it tries to apply the first patch. Since the patch's client version is less than the shadow's client version, 
    // it ignores the patch as a duplicate (that was applied earlier)
    // Then it tries to apply the second patch. This patch's client version is the same as the shadow's client version, 
    // so it applies it as with normal operation.
    // After the applying the 2nd patch, the server shadow's server version is incremented.
    //   - Primary shadow: serverVersion = 0, clientVersion = 2
    //   - Backup shadow : serverVersion = 0, clientVersion = 2
    // Finally, the server performs a diff against the shadow (whose server version is 0 and whose client version is 2).
    // Therefore, the patch produced should have client version 2, server version 0.
    // After the diff, the server version will be 1, but there's no way to verify that, except to perform another patch.
    VersionedPatch diff = sync.diff(patched);
    // the server is acknowledging client version 1 and 2 (the client should be at that version by this time)
    assertEquals(2, diff.getClientVersion());
    // the server created the patch against server version 0 (but it will be 1 after the patch is created)
    assertEquals(0, diff.getServerVersion());
    // After the diff is applied, the server shadow's server version is incremented.
    //   - Primary shadow: serverVersion = 1, clientVersion = 2
    //   - Backup shadow : serverVersion = 0, clientVersion = 2
    // Now test that the resulting list is as expected.
    // The original should remain unchanged
    assertEquals(todos, getTodoList());
    // The patched resource should now contain 2 additional items, one from each patch sent.
    // It should *NOT* have two of the item that was added as part of the initial patch (the one that was sent twice).
    assertNotEquals(patched, todos);
    // Should only have added 2 new items. It shouldn't have added the first new item twice.
    assertEquals(5, patched.size());
    assertEquals(todos.get(0), patched.get(0));
    assertEquals(todos.get(1), patched.get(1));
    assertEquals(todos.get(2), patched.get(2));
    assertEquals(new Todo(100L, "NEW ITEM 100", false), patched.get(3));
    assertEquals(new Todo(200L, "NEW ITEM 200", false), patched.get(4));
}
Also used : Todo(org.springframework.sync.Todo) MapBasedShadowStore(org.springframework.sync.diffsync.shadowstore.MapBasedShadowStore) ArrayList(java.util.ArrayList) PatchOperation(org.springframework.sync.PatchOperation) AddOperation(org.springframework.sync.AddOperation) Test(org.junit.Test)

Aggregations

ArrayList (java.util.ArrayList)2 AddOperation (org.springframework.sync.AddOperation)2 PatchOperation (org.springframework.sync.PatchOperation)2 JsonNode (com.fasterxml.jackson.databind.JsonNode)1 ArrayNode (com.fasterxml.jackson.databind.node.ArrayNode)1 Test (org.junit.Test)1 CopyOperation (org.springframework.sync.CopyOperation)1 MoveOperation (org.springframework.sync.MoveOperation)1 Patch (org.springframework.sync.Patch)1 PatchException (org.springframework.sync.PatchException)1 RemoveOperation (org.springframework.sync.RemoveOperation)1 ReplaceOperation (org.springframework.sync.ReplaceOperation)1 TestOperation (org.springframework.sync.TestOperation)1 Todo (org.springframework.sync.Todo)1 MapBasedShadowStore (org.springframework.sync.diffsync.shadowstore.MapBasedShadowStore)1