Skip to content

Commit 809b829

Browse files
hermanliangrogerhu
authored andcommitted
Attempt to re-save installation if Installation was deleted on the server (#579)
* Attempt to re-save installation if Installation was deleted on the server * add test * minor change of verification
1 parent aed49e2 commit 809b829

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

Parse/src/main/java/com/parse/ParseInstallation.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,28 @@ public Task<T> then(Task<Void> task) throws Exception {
137137
}
138138
}
139139

140+
@Override
141+
/* package */ Task<Void> saveAsync(final String sessionToken, final Task<Void> toAwait) {
142+
return super.saveAsync(sessionToken, toAwait).continueWithTask(new Continuation<Void, Task<Void>>() {
143+
@Override
144+
public Task<Void> then(Task<Void> task) throws Exception {
145+
// Retry the fetch as a save operation because this Installation was deleted on the server.
146+
// Do not attempt to resave an object if LDS is enabled, since changing objectId is not allowed.
147+
if(!Parse.isLocalDatastoreEnabled()
148+
&& task.getError() != null
149+
&& task.getError() instanceof ParseException
150+
&& ((ParseException) task.getError()).getCode() == ParseException.OBJECT_NOT_FOUND) {
151+
synchronized (mutex) {
152+
setObjectId(null);
153+
markAllFieldDirty();
154+
return ParseInstallation.super.saveAsync(sessionToken, toAwait);
155+
}
156+
}
157+
return task;
158+
}
159+
});
160+
}
161+
140162
@Override
141163
/* package */ Task<Void> handleSaveResultAsync(ParseObject.State result,
142164
ParseOperationSet operationsBeforeSave) {

Parse/src/main/java/com/parse/ParseObject.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2815,6 +2815,15 @@ private void rebuildEstimatedData() {
28152815
}
28162816
}
28172817

2818+
/* package */ void markAllFieldDirty() {
2819+
synchronized (mutex) {
2820+
estimatedData.clear();
2821+
for (String key : state.keySet()) {
2822+
performPut(key, state.get(key));
2823+
}
2824+
}
2825+
}
2826+
28182827
/**
28192828
* performOperation() is like {@link #put(String, Object)} but instead of just taking a new value,
28202829
* it takes a ParseFieldOperation that modifies the value.

Parse/src/test/java/com/parse/ParseInstallationTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929
import bolts.Task;
3030

3131
import static org.junit.Assert.assertEquals;
32+
import static org.junit.Assert.assertNotNull;
3233
import static org.junit.Assert.assertNull;
3334
import static org.junit.Assert.assertTrue;
3435
import static org.mockito.Matchers.any;
36+
import static org.mockito.Matchers.eq;
3537
import static org.mockito.Mockito.doReturn;
3638
import static org.mockito.Mockito.mock;
3739
import static org.mockito.Mockito.spy;
@@ -108,6 +110,50 @@ public void testImmutableKeys() {
108110
}
109111
}
110112

113+
@Test
114+
public void testSaveAsync() throws Exception {
115+
String sessionToken = "sessionToken";
116+
Task<Void> toAwait = Task.forResult(null);
117+
118+
ParseCurrentInstallationController controller =
119+
mock(ParseCurrentInstallationController.class);
120+
ParseInstallation currentInstallation = new ParseInstallation();
121+
when(controller.getAsync())
122+
.thenReturn(Task.forResult(currentInstallation));
123+
ParseCorePlugins.getInstance()
124+
.registerCurrentInstallationController(controller);
125+
ParseObject.State state = new ParseObject.State.Builder("_Installation")
126+
.put("deviceToken", "deviceToken")
127+
.build();
128+
ParseInstallation installation = ParseInstallation.getCurrentInstallation();
129+
assertNotNull(installation);
130+
installation.setState(state);
131+
132+
ParseObjectController objController = mock(ParseObjectController.class);
133+
// mock return task when Installation was deleted on the server
134+
Task<ParseObject.State> taskError = Task.forError(new ParseException(ParseException.OBJECT_NOT_FOUND, ""));
135+
// mock return task when Installation was re-saved to the server
136+
Task<ParseObject.State> task = Task.forResult(null);
137+
when(objController.saveAsync(
138+
any(ParseObject.State.class),
139+
any(ParseOperationSet.class),
140+
eq(sessionToken),
141+
any(ParseDecoder.class)))
142+
.thenReturn(taskError)
143+
.thenReturn(task);
144+
ParseCorePlugins.getInstance()
145+
.registerObjectController(objController);
146+
147+
installation.saveAsync(sessionToken, toAwait);
148+
149+
verify(controller).getAsync();
150+
verify(objController, times(2)).saveAsync(
151+
any(ParseObject.State.class),
152+
any(ParseOperationSet.class),
153+
eq(sessionToken),
154+
any(ParseDecoder.class));
155+
}
156+
111157
@Test
112158
public void testHandleSaveResultAsync() throws Exception {
113159
// Mock currentInstallationController to make setAsync work

0 commit comments

Comments
 (0)