From db9312c5a671e24eb8dd69abf3003c88c1520c54 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Wed, 1 Feb 2017 14:21:14 +0800 Subject: [PATCH 1/3] Attempt to re-save installation if Installation was deleted on the server --- .../java/com/parse/ParseInstallation.java | 22 +++++++++++++++++++ .../src/main/java/com/parse/ParseObject.java | 9 ++++++++ 2 files changed, 31 insertions(+) diff --git a/Parse/src/main/java/com/parse/ParseInstallation.java b/Parse/src/main/java/com/parse/ParseInstallation.java index 70019e314..b84bdf185 100644 --- a/Parse/src/main/java/com/parse/ParseInstallation.java +++ b/Parse/src/main/java/com/parse/ParseInstallation.java @@ -137,6 +137,28 @@ public Task then(Task task) throws Exception { } } + @Override + /* package */ Task saveAsync(final String sessionToken, final Task toAwait) { + return super.saveAsync(sessionToken, toAwait).continueWithTask(new Continuation>() { + @Override + public Task then(Task task) throws Exception { + // Retry the fetch as a save operation because this Installation was deleted on the server. + // Do not attempt to resave an object if LDS is enabled, since changing objectId is not allowed. + if(!Parse.isLocalDatastoreEnabled() + && task.getError() != null + && task.getError() instanceof ParseException + && ((ParseException) task.getError()).getCode() == ParseException.OBJECT_NOT_FOUND) { + synchronized (mutex) { + setObjectId(null); + markAllFieldDirty(); + return ParseInstallation.super.saveAsync(sessionToken, toAwait); + } + } + return task; + } + }); + } + @Override /* package */ Task handleSaveResultAsync(ParseObject.State result, ParseOperationSet operationsBeforeSave) { diff --git a/Parse/src/main/java/com/parse/ParseObject.java b/Parse/src/main/java/com/parse/ParseObject.java index be28b75ed..699e9dda9 100644 --- a/Parse/src/main/java/com/parse/ParseObject.java +++ b/Parse/src/main/java/com/parse/ParseObject.java @@ -2815,6 +2815,15 @@ private void rebuildEstimatedData() { } } + /* package */ void markAllFieldDirty() { + synchronized (mutex) { + estimatedData.clear(); + for (String key : state.keySet()) { + performPut(key, state.get(key)); + } + } + } + /** * performOperation() is like {@link #put(String, Object)} but instead of just taking a new value, * it takes a ParseFieldOperation that modifies the value. From 713e31121f29e96e597c9ed5f3f6d8faaa2e630a Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Mon, 13 Mar 2017 10:20:40 +0800 Subject: [PATCH 2/3] add test --- .../java/com/parse/ParseInstallationTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Parse/src/test/java/com/parse/ParseInstallationTest.java b/Parse/src/test/java/com/parse/ParseInstallationTest.java index f71ac0de1..3144916ed 100644 --- a/Parse/src/test/java/com/parse/ParseInstallationTest.java +++ b/Parse/src/test/java/com/parse/ParseInstallationTest.java @@ -29,9 +29,11 @@ import bolts.Task; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -108,6 +110,50 @@ public void testImmutableKeys() { } } + @Test + public void testSaveAsync() throws Exception { + String sessionToken = "sessionToken"; + Task toAwait = Task.forResult(null); + + ParseCurrentInstallationController controller = + mock(ParseCurrentInstallationController.class); + ParseInstallation currentInstallation = new ParseInstallation(); + when(controller.getAsync()) + .thenReturn(Task.forResult(currentInstallation)); + ParseCorePlugins.getInstance() + .registerCurrentInstallationController(controller); + ParseObject.State state = new ParseObject.State.Builder("_Installation") + .put("deviceToken", "deviceToken") + .build(); + ParseInstallation installation = ParseInstallation.getCurrentInstallation(); + assertNotNull(installation); + installation.setState(state); + + ParseObjectController objController = mock(ParseObjectController.class); + // mock return task when Installation was deleted on the server + Task taskError = Task.forError(new ParseException(ParseException.OBJECT_NOT_FOUND, "")); + // mock return task when Installation was re-saved to the server + Task task = Task.forResult(null); + when(objController.saveAsync( + any(ParseObject.State.class), + any(ParseOperationSet.class), + eq(sessionToken), + any(ParseDecoder.class))) + .thenReturn(taskError) + .thenReturn(task); + ParseCorePlugins.getInstance() + .registerObjectController(objController); + + installation.saveAsync(sessionToken, toAwait); + + verify(controller, times(1)).getAsync(); + verify(objController, times(2)).saveAsync( + any(ParseObject.State.class), + any(ParseOperationSet.class), + eq(sessionToken), + any(ParseDecoder.class)); + } + @Test public void testHandleSaveResultAsync() throws Exception { // Mock currentInstallationController to make setAsync work From 94da91139e7a255c005362b01ee40e71fe130435 Mon Sep 17 00:00:00 2001 From: Herman Liang Date: Mon, 13 Mar 2017 11:31:54 +0800 Subject: [PATCH 3/3] minor change of verification --- Parse/src/test/java/com/parse/ParseInstallationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parse/src/test/java/com/parse/ParseInstallationTest.java b/Parse/src/test/java/com/parse/ParseInstallationTest.java index 3144916ed..ecb3e5748 100644 --- a/Parse/src/test/java/com/parse/ParseInstallationTest.java +++ b/Parse/src/test/java/com/parse/ParseInstallationTest.java @@ -146,7 +146,7 @@ public void testSaveAsync() throws Exception { installation.saveAsync(sessionToken, toAwait); - verify(controller, times(1)).getAsync(); + verify(controller).getAsync(); verify(objController, times(2)).saveAsync( any(ParseObject.State.class), any(ParseOperationSet.class),