Skip to content

Commit e3598ae

Browse files
authored
Merge pull request #1661 from dimagi/ml/purge-old-cases-on-sync
Purge old cases more often
2 parents b4e3ea7 + 8546c90 commit e3598ae

File tree

8 files changed

+191
-4
lines changed

8 files changed

+191
-4
lines changed

src/main/java/org/commcare/formplayer/services/RestoreFactory.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import java.net.URI;
6565
import java.sql.SQLException;
6666
import java.time.Duration;
67+
import java.util.Arrays;
6768
import java.util.Collections;
6869
import java.util.HashMap;
6970
import java.util.LinkedHashMap;
@@ -157,6 +158,7 @@ public class RestoreFactory {
157158
private boolean hasRestored;
158159
private String caseId;
159160
private boolean configured = false;
161+
private boolean hasLocationChanged = false;
160162

161163
public void configure(String domain, String caseId, HqAuth auth) {
162164
this.setUsername(UserUtils.getRestoreAsCaseIdUsername(caseId));
@@ -217,14 +219,14 @@ public UserSqlSandbox performTimedSync(boolean shouldPurge, boolean skipFixtures
217219
}
218220
UserSqlSandbox sandbox;
219221
try {
220-
sandbox = restoreUser(skipFixtures);
222+
sandbox = restoreUser(skipFixtures, shouldPurge);
221223
} catch (HttpClientErrorException e) {
222224
if (e.getStatusCode().value() == 412) {
223225
return handle412Sync(shouldPurge, skipFixtures);
224226
}
225227
throw e;
226228
}
227-
if (shouldPurge && sandbox != null) {
229+
if (sandbox != null && (shouldPurge || hasLocationChanged)) {
228230
try {
229231
SimpleTimer purgeTimer = new SimpleTimer();
230232
purgeTimer.start();
@@ -278,7 +280,7 @@ public UserSqlSandbox getSandbox() throws SyncRestoreException {
278280
}
279281

280282
@Trace
281-
private UserSqlSandbox restoreUser(boolean skipFixtures) throws SyncRestoreException {
283+
private UserSqlSandbox restoreUser(boolean skipFixtures, boolean shouldPurge) throws SyncRestoreException {
282284
PrototypeFactory.setStaticHasher(new ClassNameHasher());
283285

284286
// create extras to send to category timing helper
@@ -292,6 +294,10 @@ private UserSqlSandbox restoreUser(boolean skipFixtures) throws SyncRestoreExcep
292294
UserSqlSandbox sandbox = getSqlSandbox();
293295
FormplayerTransactionParserFactory factory = new FormplayerTransactionParserFactory(sandbox, true);
294296
InputStream restoreStream = getRestoreXml(skipFixtures);
297+
String oldSandboxLocations = "";
298+
if (!shouldPurge) {
299+
oldSandboxLocations = UserUtils.getUserLocationsByDomain(domain, sandbox);
300+
}
295301

296302
SimpleTimer parseTimer = new SimpleTimer();
297303
parseTimer.start();
@@ -310,6 +316,15 @@ private UserSqlSandbox restoreUser(boolean skipFixtures) throws SyncRestoreExcep
310316
extras
311317
);
312318
sandbox.writeSyncToken();
319+
320+
if (!shouldPurge && !oldSandboxLocations.isEmpty()) {
321+
String newSandboxLocations = UserUtils.getUserLocationsByDomain(domain, sandbox);
322+
String[] oldArray = oldSandboxLocations.split(" ");
323+
String[] newArray = newSandboxLocations.split(" ");
324+
Arrays.sort(oldArray);
325+
Arrays.sort(newArray);
326+
hasLocationChanged = !Arrays.equals(oldArray, newArray);
327+
}
313328
return sandbox;
314329
} catch (InvalidStructureException | SQLiteRuntimeException e) {
315330
if (e instanceof InvalidStructureException || ++counter >= maxRetries) {
@@ -361,6 +376,10 @@ public SQLiteDB getSQLiteDB() {
361376
return sqLiteDB;
362377
}
363378

379+
public boolean getHasLocationChanged() {
380+
return hasLocationChanged;
381+
}
382+
364383
public String getWrappedUsername() {
365384
return asUsername == null ? username : asUsername;
366385
}

src/main/java/org/commcare/formplayer/util/UserUtils.java

+19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package org.commcare.formplayer.util;
22

3+
import org.commcare.formplayer.sandbox.SqlStorage;
4+
import org.commcare.formplayer.sandbox.UserSqlSandbox;
5+
import org.javarosa.core.model.User;
6+
7+
import java.util.Iterator;
8+
39
/**
410
* Utility methods for dealing with users
511
*/
@@ -26,4 +32,17 @@ public static String getShortUsername(String username, String domain) {
2632
return username;
2733
}
2834
}
35+
36+
public static String getUserLocationsByDomain(String domain, UserSqlSandbox sandbox) {
37+
SqlStorage<User> userStorage = sandbox.getUserStorage();
38+
Iterator userIterator = userStorage.iterator();
39+
while (userIterator.hasNext()) {
40+
User uUser = (User)userIterator.next();
41+
String userDomain = uUser.getProperty("commcare_project");
42+
if (userDomain != null && userDomain.equals(domain)) {
43+
return uUser.getProperty("commcare_location_ids");
44+
}
45+
}
46+
return "";
47+
}
2948
}

src/test/java/org/commcare/formplayer/tests/RestoreFactoryTest.java

+39-1
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
import org.commcare.formplayer.auth.DjangoAuth;
1515
import org.commcare.formplayer.beans.AuthenticatedRequestBean;
1616
import org.commcare.formplayer.configuration.CacheConfiguration;
17+
import org.commcare.formplayer.junit.RestoreFactoryAnswer;
18+
import org.commcare.formplayer.sandbox.SqlStorage;
19+
import org.commcare.formplayer.sandbox.UserSqlSandbox;
1720
import org.commcare.formplayer.services.RestoreFactory;
1821
import org.commcare.formplayer.util.Constants;
1922
import org.commcare.formplayer.util.RequestUtils;
2023
import org.commcare.formplayer.utils.TestContext;
24+
import org.commcare.formplayer.util.UserUtils;
2125
import org.commcare.formplayer.utils.WithHqUser;
2226
import org.hamcrest.Description;
2327
import org.hamcrest.Matcher;
@@ -51,7 +55,7 @@
5155
*/
5256
@WebMvcTest
5357
@ContextConfiguration(classes = {TestContext.class, CacheConfiguration.class})
54-
public class RestoreFactoryTest {
58+
public class RestoreFactoryTest extends BaseTestClass {
5559

5660
private static final String BASE_URL = "http://localhost:8000/a/restore-domain/phone/restore/";
5761
private String username = "restore-dude";
@@ -74,6 +78,7 @@ public class RestoreFactoryTest {
7478
public void setUp() throws Exception {
7579
MockitoAnnotations.openMocks(this);
7680
Mockito.reset(restoreFactorySpy);
81+
Mockito.reset(restoreFactoryMock);
7782
AuthenticatedRequestBean requestBean = new AuthenticatedRequestBean();
7883
requestBean.setRestoreAs(asUsername);
7984
requestBean.setUsername(username);
@@ -311,6 +316,39 @@ public void testGetRequestHeaders_UseHmacAuthEvenIfHqAuthPresent() throws Except
311316
);
312317
}
313318

319+
@Test
320+
public void testChecksForLocationChanges() throws Exception {
321+
RestoreFactoryAnswer beforeChange = new RestoreFactoryAnswer("restores/location_update1.xml");
322+
Mockito.doAnswer(beforeChange).when(restoreFactoryMock).getRestoreXml(false);
323+
324+
UserSqlSandbox beforeSandbox = restoreFactoryMock.performTimedSync(false, false, false);
325+
Assertions.assertFalse(restoreFactoryMock.getHasLocationChanged());
326+
Assertions.assertEquals("testLocationId1", UserUtils.getUserLocationsByDomain(domain, beforeSandbox));
327+
328+
RestoreFactoryAnswer afterChange = new RestoreFactoryAnswer("restores/location_update2.xml");
329+
Mockito.doAnswer(afterChange).when(restoreFactoryMock).getRestoreXml(false);
330+
331+
UserSqlSandbox afterSandbox = restoreFactoryMock.performTimedSync(false, false, false);
332+
Assertions.assertTrue(restoreFactoryMock.getHasLocationChanged());
333+
Assertions.assertEquals("testLocationId2", UserUtils.getUserLocationsByDomain(domain, afterSandbox));
334+
}
335+
336+
@Test
337+
public void testSameLocationsDoesntFlagChange() throws Exception {
338+
RestoreFactoryAnswer beforeChange = new RestoreFactoryAnswer("restores/location_update3.xml");
339+
Mockito.doAnswer(beforeChange).when(restoreFactoryMock).getRestoreXml(false);
340+
341+
UserSqlSandbox beforeSandbox = restoreFactoryMock.performTimedSync(false, false, false);
342+
Assertions.assertEquals("testLocationId1 testLocationId2", UserUtils.getUserLocationsByDomain(domain, beforeSandbox));
343+
344+
RestoreFactoryAnswer afterChange = new RestoreFactoryAnswer("restores/location_update4.xml");
345+
Mockito.doAnswer(afterChange).when(restoreFactoryMock).getRestoreXml(false);
346+
347+
UserSqlSandbox afterSandbox = restoreFactoryMock.performTimedSync(false, false, false);
348+
Assertions.assertFalse(restoreFactoryMock.getHasLocationChanged());
349+
Assertions.assertEquals("testLocationId2 testLocationId1", UserUtils.getUserLocationsByDomain(domain, afterSandbox));
350+
}
351+
314352
private void validateHeaders(HttpHeaders headers,
315353
List<Matcher<Map<? extends String, ? extends List<String>>>> matchers) {
316354
for (Matcher<Map<? extends String, ? extends List<String>>> matcher : matchers) {

src/test/java/org/commcare/formplayer/tests/UserUtilsTests.java

+19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
44

5+
import org.commcare.formplayer.sandbox.SqlStorage;
6+
import org.commcare.formplayer.sandbox.UserSqlSandbox;
7+
import org.commcare.formplayer.sqlitedb.UserDB;
58
import org.commcare.formplayer.util.UserUtils;
9+
10+
import org.javarosa.core.model.User;
611
import org.junit.jupiter.api.Test;
712

813
public class UserUtilsTests {
@@ -16,4 +21,18 @@ public void testGetShortUsername() {
1621
assertEquals("[email protected]",
1722
UserUtils.getShortUsername("[email protected]", "example"));
1823
}
24+
25+
@Test
26+
public void testGetUserLocations() {
27+
String testLocationId = "testLocationId";
28+
String testDomain = "testDomain";
29+
User user = new User("test_user", "password_hash", "test_uuid");
30+
user.setProperty("commcare_location_ids", testLocationId);
31+
user.setProperty("commcare_project", testDomain);
32+
33+
UserSqlSandbox sandbox = new UserSqlSandbox(new UserDB("a", "b", null));
34+
SqlStorage<User> userStorage = sandbox.getUserStorage();
35+
userStorage.write(user);
36+
assertEquals(testLocationId, UserUtils.getUserLocationsByDomain(testDomain, sandbox));
37+
}
1938
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<OpenRosaResponse xmlns="http://openrosa.org/http/response">
2+
<message nature="ota_restore_success">Successfully restored account restore-gal!</message>
3+
<Sync xmlns="http://commcarehq.org/sync">
4+
<restore_id>9e5bb063c1264e0fa93f560f481220ae</restore_id>
5+
</Sync>
6+
<Registration xmlns="http://openrosa.org/user/registration">
7+
<username>restore-dude</username>
8+
<password>
9+
sha1$4JSloCqp4bcl$9123edc2ac2fba8b403c7366b8c72806662a188c
10+
</password>
11+
<uuid>47ef5ea3cc348fb6538702449c855dcd</uuid>
12+
<date>2025-01-28</date>
13+
<user_data>
14+
<data key="test">1</data>
15+
<data key="commcare_project">restore-domain</data>
16+
<data key="commcare_last_name"/>
17+
<data key="commcare_phone_number"/>
18+
<data key="commcare_location_id">testLocationId1</data>
19+
<data key="commcare_location_ids">testLocationId1</data>
20+
<data key="commcare_first_name"/>
21+
</user_data>
22+
</Registration>
23+
</OpenRosaResponse>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<OpenRosaResponse xmlns="http://openrosa.org/http/response">
2+
<message nature="ota_restore_success">Successfully restored account restore-gal!</message>
3+
<Sync xmlns="http://commcarehq.org/sync">
4+
<restore_id>9e5bb063c1264e0fa93f560f481220af</restore_id>
5+
</Sync>
6+
<Registration xmlns="http://openrosa.org/user/registration">
7+
<username>restore-dude</username>
8+
<password>
9+
sha1$4JSloCqp4bcl$9123edc2ac2fba8b403c7366b8c72806662a188c
10+
</password>
11+
<uuid>47ef5ea3cc348fb6538702449c855dcd</uuid>
12+
<date>2025-01-28</date>
13+
<user_data>
14+
<data key="test">1</data>
15+
<data key="commcare_project">restore-domain</data>
16+
<data key="commcare_last_name"/>
17+
<data key="commcare_phone_number"/>
18+
<data key="commcare_location_id">testLocationId2</data>
19+
<data key="commcare_location_ids">testLocationId2</data>
20+
<data key="commcare_first_name"/>
21+
</user_data>
22+
</Registration>
23+
</OpenRosaResponse>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<OpenRosaResponse xmlns="http://openrosa.org/http/response">
2+
<message nature="ota_restore_success">Successfully restored account restore-gal!</message>
3+
<Sync xmlns="http://commcarehq.org/sync">
4+
<restore_id>9e5bb063c1264e0fa93f560f481220ae</restore_id>
5+
</Sync>
6+
<Registration xmlns="http://openrosa.org/user/registration">
7+
<username>restore-dude</username>
8+
<password>
9+
sha1$4JSloCqp4bcl$9123edc2ac2fba8b403c7366b8c72806662a188c
10+
</password>
11+
<uuid>47ef5ea3cc348fb6538702449c855dcd</uuid>
12+
<date>2025-01-28</date>
13+
<user_data>
14+
<data key="test">1</data>
15+
<data key="commcare_project">restore-domain</data>
16+
<data key="commcare_last_name"/>
17+
<data key="commcare_phone_number"/>
18+
<data key="commcare_location_id">testLocationId1</data>
19+
<data key="commcare_location_ids">testLocationId1 testLocationId2</data>
20+
<data key="commcare_first_name"/>
21+
</user_data>
22+
</Registration>
23+
</OpenRosaResponse>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<OpenRosaResponse xmlns="http://openrosa.org/http/response">
2+
<message nature="ota_restore_success">Successfully restored account restore-gal!</message>
3+
<Sync xmlns="http://commcarehq.org/sync">
4+
<restore_id>9e5bb063c1264e0fa93f560f481220ae</restore_id>
5+
</Sync>
6+
<Registration xmlns="http://openrosa.org/user/registration">
7+
<username>restore-dude</username>
8+
<password>
9+
sha1$4JSloCqp4bcl$9123edc2ac2fba8b403c7366b8c72806662a188c
10+
</password>
11+
<uuid>47ef5ea3cc348fb6538702449c855dcd</uuid>
12+
<date>2025-01-28</date>
13+
<user_data>
14+
<data key="test">1</data>
15+
<data key="commcare_project">restore-domain</data>
16+
<data key="commcare_last_name"/>
17+
<data key="commcare_phone_number"/>
18+
<data key="commcare_location_id">testLocationId1</data>
19+
<data key="commcare_location_ids">testLocationId2 testLocationId1</data>
20+
<data key="commcare_first_name"/>
21+
</user_data>
22+
</Registration>
23+
</OpenRosaResponse>

0 commit comments

Comments
 (0)