Search in sources :

Example 1 with PatchOperation

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

the class DiffSyncTest method patchEntity_moveProperty.

@Test
public void patchEntity_moveProperty() throws Exception {
    DiffSync<Person> sync = new DiffSync<Person>(new MapBasedShadowStore("x"), Person.class);
    List<PatchOperation> ops = new ArrayList<PatchOperation>();
    ops.add(new MoveOperation("/firstName", "/lastName"));
    Patch patch = new Patch(ops);
    Person person = new Person("Edmund", "Blackadder");
    Person patched = sync.apply(person, patch);
    assertEquals("Blackadder", patched.getFirstName());
    assertNull(patched.getLastName());
}
Also used : MapBasedShadowStore(org.springframework.sync.diffsync.shadowstore.MapBasedShadowStore) MoveOperation(org.springframework.sync.MoveOperation) ArrayList(java.util.ArrayList) PatchOperation(org.springframework.sync.PatchOperation) Person(org.springframework.sync.Person) Patch(org.springframework.sync.Patch) Test(org.junit.Test)

Example 2 with PatchOperation

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

the class JsonPatchPatchConverter method convert.

/**
	 * Renders a {@link Patch} as a {@link JsonNode}.
	 * @param patch the patch
	 * @return a {@link JsonNode} containing JSON Patch.
	 */
public JsonNode convert(Patch patch) {
    List<PatchOperation> operations = patch.getOperations();
    JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
    ArrayNode patchNode = nodeFactory.arrayNode();
    for (PatchOperation operation : operations) {
        ObjectNode opNode = nodeFactory.objectNode();
        opNode.set("op", nodeFactory.textNode(operation.getOp()));
        opNode.set("path", nodeFactory.textNode(operation.getPath()));
        if (operation instanceof FromOperation) {
            FromOperation fromOp = (FromOperation) operation;
            opNode.set("from", nodeFactory.textNode(fromOp.getFrom()));
        }
        Object value = operation.getValue();
        if (value != null) {
            opNode.set("value", MAPPER.valueToTree(value));
        }
        patchNode.add(opNode);
    }
    return patchNode;
}
Also used : ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) FromOperation(org.springframework.sync.FromOperation) PatchOperation(org.springframework.sync.PatchOperation) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) JsonNodeFactory(com.fasterxml.jackson.databind.node.JsonNodeFactory)

Example 3 with PatchOperation

use of org.springframework.sync.PatchOperation 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 4 with PatchOperation

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

the class DiffSyncTest method patchEntity_moveProperty_lostReturnPacket.

@Test
public void patchEntity_moveProperty_lostReturnPacket() throws Exception {
    DiffSync<Person> sync = new DiffSync<Person>(new MapBasedShadowStore("x"), Person.class);
    Person person = new Person("Edmund", "Blackadder");
    List<PatchOperation> ops1 = new ArrayList<PatchOperation>();
    ops1.add(new MoveOperation("/firstName", "/lastName"));
    VersionedPatch vPatch1 = new VersionedPatch(ops1, 0, 0);
    Person patched = sync.apply(person, vPatch1);
    assertEquals("Blackadder", patched.getFirstName());
    assertNull(patched.getLastName());
    VersionedPatch lostDiff = sync.diff(patched);
    assertEquals(1, lostDiff.getClientVersion());
    assertEquals(0, lostDiff.getServerVersion());
    List<PatchOperation> ops2 = new ArrayList<PatchOperation>();
    ops2.add(new MoveOperation("/lastName", "/firstName"));
    VersionedPatch vPatch2 = new VersionedPatch(ops2, 0, 1);
    patched = sync.apply(patched, vPatch1, vPatch2);
    VersionedPatch diff = sync.diff(patched);
    assertEquals(2, diff.getClientVersion());
    assertEquals(0, diff.getServerVersion());
    assertNull(patched.getFirstName());
    assertEquals("Blackadder", patched.getLastName());
}
Also used : MapBasedShadowStore(org.springframework.sync.diffsync.shadowstore.MapBasedShadowStore) MoveOperation(org.springframework.sync.MoveOperation) ArrayList(java.util.ArrayList) PatchOperation(org.springframework.sync.PatchOperation) Person(org.springframework.sync.Person) Test(org.junit.Test)

Example 5 with PatchOperation

use of org.springframework.sync.PatchOperation 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

PatchOperation (org.springframework.sync.PatchOperation)7 ArrayList (java.util.ArrayList)6 Test (org.junit.Test)5 MoveOperation (org.springframework.sync.MoveOperation)5 MapBasedShadowStore (org.springframework.sync.diffsync.shadowstore.MapBasedShadowStore)5 Person (org.springframework.sync.Person)4 ArrayNode (com.fasterxml.jackson.databind.node.ArrayNode)2 AddOperation (org.springframework.sync.AddOperation)2 Patch (org.springframework.sync.Patch)2 JsonNode (com.fasterxml.jackson.databind.JsonNode)1 JsonNodeFactory (com.fasterxml.jackson.databind.node.JsonNodeFactory)1 ObjectNode (com.fasterxml.jackson.databind.node.ObjectNode)1 CopyOperation (org.springframework.sync.CopyOperation)1 FromOperation (org.springframework.sync.FromOperation)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