Skip to content

Commit 5604baf

Browse files
authored
Merge pull request #1404 from OneSignal/fix/EXTERNAL_USER_ID_AUTH_HASH-not-cleared
Fix `EXTERNAL_USER_ID_AUTH_HASH` not popped and old one sent to backend
2 parents 68d6305 + 3c7e568 commit 5604baf

File tree

2 files changed

+319
-1
lines changed

2 files changed

+319
-1
lines changed

OneSignalSDK/onesignal/src/main/java/com/onesignal/UserState.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import static com.onesignal.UserStateSynchronizer.APP_ID;
1717
import static com.onesignal.UserStateSynchronizer.EMAIL_AUTH_HASH_KEY;
18+
import static com.onesignal.UserStateSynchronizer.EXTERNAL_USER_ID;
1819
import static com.onesignal.UserStateSynchronizer.EXTERNAL_USER_ID_AUTH_HASH;
1920
import static com.onesignal.UserStateSynchronizer.SMS_AUTH_HASH_KEY;
2021

@@ -251,8 +252,9 @@ JSONObject generateJsonDiff(UserState newState, boolean isSessionCall) {
251252
sendJson.put(EMAIL_AUTH_HASH_KEY, syncValues.optString(EMAIL_AUTH_HASH_KEY));
252253
if (syncValues.has(SMS_AUTH_HASH_KEY))
253254
sendJson.put(SMS_AUTH_HASH_KEY, syncValues.optString(SMS_AUTH_HASH_KEY));
254-
if (syncValues.has(EXTERNAL_USER_ID_AUTH_HASH))
255+
if (syncValues.has(EXTERNAL_USER_ID_AUTH_HASH) && !sendJson.has(EXTERNAL_USER_ID_AUTH_HASH)) {
255256
sendJson.put(EXTERNAL_USER_ID_AUTH_HASH, syncValues.optString(EXTERNAL_USER_ID_AUTH_HASH));
257+
}
256258
} catch (JSONException e) {
257259
e.printStackTrace();
258260
}
@@ -317,6 +319,18 @@ private void loadState() {
317319

318320
void persistState() {
319321
synchronized(LOCK) {
322+
// pop the EXTERNAL_USER_ID_AUTH_HASH if in process of removing external user ID
323+
// external_user_id is either "" or not present
324+
try {
325+
if (syncValues.has(EXTERNAL_USER_ID_AUTH_HASH) &&
326+
((syncValues.has(EXTERNAL_USER_ID) && syncValues.get(EXTERNAL_USER_ID).toString() == "") || !syncValues.has(EXTERNAL_USER_ID))) {
327+
syncValues.remove(EXTERNAL_USER_ID_AUTH_HASH);
328+
// the auth_hash is popped above but external user id may still remain as ""
329+
}
330+
} catch (JSONException e) {
331+
e.printStackTrace();
332+
}
333+
320334
OneSignalPrefs.saveString(OneSignalPrefs.PREFS_ONESIGNAL,
321335
OneSignalPrefs.PREFS_ONESIGNAL_USERSTATE_SYNCVALYES_ + persistKey, syncValues.toString());
322336
OneSignalPrefs.saveString(OneSignalPrefs.PREFS_ONESIGNAL,

OneSignalSDK/unittest/src/test/java/com/test/onesignal/SynchronizerIntegrationTests.java

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import android.annotation.SuppressLint;
44
import android.app.Activity;
5+
import android.content.Context;
6+
import android.content.SharedPreferences;
57

68
import androidx.test.core.app.ApplicationProvider;
79

@@ -1470,6 +1472,250 @@ public void shouldRemoveExternalUserIdFromPushAndEmailAndSMSWithAuthHash() throw
14701472
assertEquals(mockSMSHash, removeIdSMSRequest.payload.getString("sms_auth_hash"));
14711473
}
14721474

1475+
// The external_user_id_auth_hash should be removed from the user state
1476+
@Test
1477+
public void shouldPopAuthHash_afterRemoveExternalUserIdFromPushWithAuthHash() throws Exception {
1478+
String testExternalId = "test_ext_id";
1479+
String mockExternalIdHash = new String(new char[64]).replace('\0', '0');
1480+
1481+
OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null);
1482+
OneSignalInit();
1483+
threadAndTaskWait();
1484+
1485+
// Check that external_user_id_auth_hash is in syncValues
1486+
TestHelpers.flushBufferedSharedPrefs();
1487+
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1488+
JSONObject syncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1489+
assertEquals(testExternalId, syncValues.getString("external_user_id"));
1490+
assertEquals(mockExternalIdHash, syncValues.getString("external_user_id_auth_hash"));
1491+
1492+
// Call to remove external user ID
1493+
OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler());
1494+
threadAndTaskWait();
1495+
1496+
JSONObject expectedExternalUserIdResponse = new JSONObject(
1497+
"{" +
1498+
" \"push\" : {" +
1499+
" \"success\" : true" +
1500+
" }" +
1501+
"}"
1502+
);
1503+
assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString());
1504+
1505+
assertEquals(3, ShadowOneSignalRestClient.networkCallCount);
1506+
1507+
ShadowOneSignalRestClient.Request removeIdRequest = ShadowOneSignalRestClient.requests.get(2);
1508+
assertEquals(ShadowOneSignalRestClient.REST_METHOD.PUT, removeIdRequest.method);
1509+
assertEquals(removeIdRequest.payload.getString("external_user_id"), "");
1510+
assertEquals(mockExternalIdHash, removeIdRequest.payload.getString("external_user_id_auth_hash"));
1511+
1512+
// Check that external_user_id_auth_hash is no longer in syncValues and has "" as external_user_id
1513+
TestHelpers.flushBufferedSharedPrefs();
1514+
final SharedPreferences newPrefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1515+
syncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1516+
assertFalse(syncValues.has("external_user_id_auth_hash"));
1517+
assertEquals("", syncValues.getString("external_user_id"));
1518+
}
1519+
1520+
@Test
1521+
public void shouldPopAuthHash_afterRemoveExternalUserIdFromPushAndEmailWithAuthHash() throws Exception {
1522+
String testExternalId = "test_ext_id";
1523+
String mockExternalIdHash = new String(new char[64]).replace('\0', '0');
1524+
String testEmail = "test@test.com";
1525+
String mockEmailHash = new String(new char[64]).replace('\0', '1');
1526+
1527+
// 1. Init OneSignal
1528+
OneSignalInit();
1529+
threadAndTaskWait();
1530+
1531+
// 2. Attempt to set email and email hash
1532+
OneSignal.setEmail(testEmail, mockEmailHash, getEmailUpdateHandler());
1533+
threadAndTaskWait();
1534+
1535+
// 3. Attempt to set external Id with auth hash
1536+
OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null);
1537+
threadAndTaskWait();
1538+
1539+
// 4. Check the user state for push and email
1540+
TestHelpers.flushBufferedSharedPrefs();
1541+
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1542+
JSONObject pushSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1543+
JSONObject emailSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_emailCURRENT_STATE", null));
1544+
1545+
assertEquals(testExternalId, pushSyncValues.getString("external_user_id"));
1546+
assertEquals(mockExternalIdHash, pushSyncValues.getString("external_user_id_auth_hash"));
1547+
1548+
assertEquals(testExternalId, emailSyncValues.getString("external_user_id"));
1549+
assertEquals(mockExternalIdHash, emailSyncValues.getString("external_user_id_auth_hash"));
1550+
1551+
// 5. Call to remove external user ID
1552+
OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler());
1553+
threadAndTaskWait();
1554+
1555+
JSONObject expectedExternalUserIdResponse = new JSONObject(
1556+
"{" +
1557+
" \"push\" : {" +
1558+
" \"success\" : true" +
1559+
" }" + ", " +
1560+
" \"email\" : {" +
1561+
" \"success\" : true" +
1562+
" }" +
1563+
"}"
1564+
);
1565+
1566+
assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString());
1567+
assertTrue(didEmailUpdateSucceed);
1568+
assertNull(lastEmailUpdateFailure);
1569+
1570+
// 6. Check that external_user_id_auth_hash is no longer in syncValues and has "" as external_user_id
1571+
TestHelpers.flushBufferedSharedPrefs();
1572+
final SharedPreferences newPrefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1573+
pushSyncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1574+
emailSyncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_emailCURRENT_STATE", null));
1575+
1576+
assertFalse(pushSyncValues.has("external_user_id_auth_hash"));
1577+
assertEquals("", pushSyncValues.getString("external_user_id"));
1578+
1579+
assertFalse(emailSyncValues.has("external_user_id_auth_hash"));
1580+
assertEquals("", emailSyncValues.getString("external_user_id"));
1581+
}
1582+
1583+
@Test
1584+
public void shouldPopAuthHash_afterRemoveExternalUserIdFromPushAndSMSWithAuthHash() throws Exception {
1585+
String testExternalId = "test_ext_id";
1586+
String mockExternalIdHash = new String(new char[64]).replace('\0', '0');
1587+
String mockSMSHash = new String(new char[64]).replace('\0', '1');
1588+
1589+
// 1. Init OneSignal
1590+
OneSignalInit();
1591+
threadAndTaskWait();
1592+
1593+
// 2. Attempt to set SMS and SMS hash
1594+
OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash, null);
1595+
threadAndTaskWait();
1596+
1597+
// 3. Attempt to set external Id with auth hash
1598+
OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null);
1599+
threadAndTaskWait();
1600+
1601+
// 4. Check the user state for sms and email
1602+
TestHelpers.flushBufferedSharedPrefs();
1603+
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1604+
JSONObject pushSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1605+
JSONObject smsSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_smsCURRENT_STATE", null));
1606+
1607+
assertEquals(testExternalId, pushSyncValues.getString("external_user_id"));
1608+
assertEquals(mockExternalIdHash, pushSyncValues.getString("external_user_id_auth_hash"));
1609+
1610+
assertEquals(testExternalId, smsSyncValues.getString("external_user_id"));
1611+
assertEquals(mockExternalIdHash, smsSyncValues.getString("external_user_id_auth_hash"));
1612+
1613+
// 5. Call to remove external user ID
1614+
OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler());
1615+
threadAndTaskWait();
1616+
1617+
JSONObject expectedExternalUserIdResponse = new JSONObject(
1618+
"{" +
1619+
" \"push\" : {" +
1620+
" \"success\" : true" +
1621+
" }" + ", " +
1622+
" \"sms\" : {" +
1623+
" \"success\" : true" +
1624+
" }" +
1625+
"}"
1626+
);
1627+
1628+
assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString());
1629+
1630+
// 6. Check that external_user_id_auth_hash is no longer in syncValues and has "" as external_user_id
1631+
TestHelpers.flushBufferedSharedPrefs();
1632+
final SharedPreferences newPrefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1633+
pushSyncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1634+
smsSyncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_smsCURRENT_STATE", null));
1635+
1636+
assertFalse(pushSyncValues.has("external_user_id_auth_hash"));
1637+
assertEquals("", pushSyncValues.getString("external_user_id"));
1638+
1639+
assertFalse(smsSyncValues.has("external_user_id_auth_hash"));
1640+
assertEquals("", smsSyncValues.getString("external_user_id"));
1641+
}
1642+
1643+
@Test
1644+
public void shouldPopAuthHash_afterRemoveExternalUserIdFromPushAndEmailAndSMSWithAuthHash() throws Exception {
1645+
String testExternalId = "test_ext_id";
1646+
String mockExternalIdHash = new String(new char[64]).replace('\0', '0');
1647+
String mockEmailHash = new String(new char[64]).replace('\0', '1');
1648+
String mockSMSHash = new String(new char[64]).replace('\0', '2');
1649+
1650+
// 1. Init OneSignal
1651+
OneSignalInit();
1652+
threadAndTaskWait();
1653+
1654+
// 2. Attempt to set SMS and SMS hash
1655+
OneSignal.setSMSNumber(ONESIGNAL_SMS_NUMBER, mockSMSHash, null);
1656+
threadAndTaskWait();
1657+
1658+
// 3. Attempt to set email and email hash
1659+
OneSignal.setEmail(ONESIGNAL_EMAIL_ADDRESS, mockEmailHash, null);
1660+
threadAndTaskWait();
1661+
1662+
// 4. Attempt to set external Id with auth hash
1663+
OneSignal.setExternalUserId(testExternalId, mockExternalIdHash, null);
1664+
threadAndTaskWait();
1665+
1666+
// 5. Check the user state for sms and email and push
1667+
TestHelpers.flushBufferedSharedPrefs();
1668+
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1669+
JSONObject pushSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1670+
JSONObject smsSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_smsCURRENT_STATE", null));
1671+
JSONObject emailSyncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_emailCURRENT_STATE", null));
1672+
1673+
assertEquals(testExternalId, pushSyncValues.getString("external_user_id"));
1674+
assertEquals(mockExternalIdHash, pushSyncValues.getString("external_user_id_auth_hash"));
1675+
1676+
assertEquals(testExternalId, smsSyncValues.getString("external_user_id"));
1677+
assertEquals(mockExternalIdHash, smsSyncValues.getString("external_user_id_auth_hash"));
1678+
1679+
assertEquals(testExternalId, emailSyncValues.getString("external_user_id"));
1680+
assertEquals(mockExternalIdHash, emailSyncValues.getString("external_user_id_auth_hash"));
1681+
1682+
// 6. Call to remove external user ID
1683+
OneSignal.removeExternalUserId(getExternalUserIdUpdateCompletionHandler());
1684+
threadAndTaskWait();
1685+
1686+
JSONObject expectedExternalUserIdResponse = new JSONObject(
1687+
"{" +
1688+
" \"push\" : {" +
1689+
" \"success\" : true" +
1690+
" }," +
1691+
" \"email\" : {" +
1692+
" \"success\" : true" +
1693+
" }," +
1694+
" \"sms\" : {" +
1695+
" \"success\" : true" +
1696+
" }" +
1697+
"}"
1698+
);
1699+
1700+
assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString());
1701+
1702+
// 7. Check that external_user_id_auth_hash is no longer in syncValues and has "" as external_user_id
1703+
TestHelpers.flushBufferedSharedPrefs();
1704+
final SharedPreferences newPrefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1705+
pushSyncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1706+
smsSyncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_smsCURRENT_STATE", null));
1707+
emailSyncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_emailCURRENT_STATE", null));
1708+
1709+
assertFalse(pushSyncValues.has("external_user_id_auth_hash"));
1710+
assertEquals("", pushSyncValues.getString("external_user_id"));
1711+
1712+
assertFalse(smsSyncValues.has("external_user_id_auth_hash"));
1713+
assertEquals("", smsSyncValues.getString("external_user_id"));
1714+
1715+
assertFalse(emailSyncValues.has("external_user_id_auth_hash"));
1716+
assertEquals("", emailSyncValues.getString("external_user_id"));
1717+
}
1718+
14731719
@Test
14741720
public void doesNotSendSameExternalId() throws Exception {
14751721
String testExternalId = "test_ext_id";
@@ -1642,6 +1888,64 @@ public void sendDifferentExternalUserId_withCompletionHandler() throws Exception
16421888
assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString());
16431889
}
16441890

1891+
@Test
1892+
public void sendDifferentExternalUserId_withAuthHash_withCompletionHandler() throws Exception {
1893+
String testExternalId1 = "test_ext_id_1";
1894+
String mockExternalIdHash1 = new String(new char[64]).replace('\0', '0');
1895+
1896+
// 1. Init OneSignal
1897+
OneSignalInit();
1898+
threadAndTaskWait();
1899+
1900+
// 2. Attempt to set external user id and auth hash with callback
1901+
OneSignal.setExternalUserId(testExternalId1, mockExternalIdHash1, getExternalUserIdUpdateCompletionHandler());
1902+
threadAndTaskWait();
1903+
1904+
// 3. Make sure lastExternalUserIdResponse is equal to the expected response
1905+
JSONObject expectedExternalUserIdResponse = new JSONObject(
1906+
"{" +
1907+
" \"push\" : {" +
1908+
" \"success\" : true" +
1909+
" }" +
1910+
"}"
1911+
);
1912+
assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString());
1913+
1914+
// 4. Check the external user id and auth hash values in syncValues
1915+
TestHelpers.flushBufferedSharedPrefs();
1916+
final SharedPreferences prefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1917+
JSONObject syncValues = new JSONObject(prefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1918+
assertEquals(testExternalId1, syncValues.getString("external_user_id"));
1919+
assertEquals(mockExternalIdHash1, syncValues.getString("external_user_id_auth_hash"));
1920+
1921+
// 5. Change test external user id and auth hash to send
1922+
String testExternalId2 = "test_ext_id_2";
1923+
String mockExternalIdHash2 = new String(new char[64]).replace('\0', '1');
1924+
1925+
// 6. Attempt to set same another external user id and auth hash with callback
1926+
OneSignal.setExternalUserId(testExternalId2, mockExternalIdHash2, getExternalUserIdUpdateCompletionHandler());
1927+
threadAndTaskWait();
1928+
1929+
// 7. Make sure lastExternalUserIdResponse is equal to the expected response
1930+
assertEquals(expectedExternalUserIdResponse.toString(), lastExternalUserIdResponse.toString());
1931+
1932+
// 8. Check that the correct external user id and auth hash values were sent in the two requests
1933+
ShadowOneSignalRestClient.Request externalIdRequest1 = ShadowOneSignalRestClient.requests.get(2);
1934+
assertEquals(testExternalId1, externalIdRequest1.payload.getString("external_user_id"));
1935+
assertEquals(mockExternalIdHash1, externalIdRequest1.payload.getString("external_user_id_auth_hash"));
1936+
1937+
ShadowOneSignalRestClient.Request externalIdRequest2 = ShadowOneSignalRestClient.requests.get(3);
1938+
assertEquals(testExternalId2, externalIdRequest2.payload.getString("external_user_id"));
1939+
assertEquals(mockExternalIdHash2, externalIdRequest2.payload.getString("external_user_id_auth_hash"));
1940+
1941+
// 9. Check the external user id and auth hash values in syncValues
1942+
TestHelpers.flushBufferedSharedPrefs();
1943+
final SharedPreferences newPrefs = blankActivity.getSharedPreferences(OneSignal.class.getSimpleName(), Context.MODE_PRIVATE);
1944+
syncValues = new JSONObject(newPrefs.getString("ONESIGNAL_USERSTATE_SYNCVALYES_CURRENT_STATE", null));
1945+
assertEquals(testExternalId2, syncValues.getString("external_user_id"));
1946+
assertEquals(mockExternalIdHash2, syncValues.getString("external_user_id_auth_hash"));
1947+
}
1948+
16451949
@Test
16461950
public void sendSameExternalUserId_withCompletionHandler() throws Exception {
16471951
String testExternalId = "test_ext_id";

0 commit comments

Comments
 (0)