Skip to content

Commit

Permalink
feat: delete custom user attributes from subsequent events
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoweii authored and zxkane committed Dec 7, 2023
1 parent 7c7e3e6 commit f43e9ca
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class AnalyticsClient {
private static final Log LOG = LogFactory.getLog(AnalyticsClient.class);
private final ClickstreamContext context;
private final Map<String, Object> globalAttributes = new ConcurrentHashMap<>();
private JSONObject userAttributes;
private JSONObject simpleUserAttributes;
private JSONObject allUserAttributes;
private String userId;
private String userUniqueId;
private final EventRecorder eventRecorder;
Expand All @@ -51,7 +52,8 @@ public AnalyticsClient(@NonNull final ClickstreamContext context) {
eventRecorder = EventRecorder.newInstance(context);
userId = PreferencesUtil.getCurrentUserId(context.getSystem().getPreferences());
userUniqueId = PreferencesUtil.getCurrentUserUniqueId(context.getSystem().getPreferences());
userAttributes = PreferencesUtil.getUserAttribute(context.getSystem().getPreferences());
allUserAttributes = PreferencesUtil.getUserAttribute(context.getSystem().getPreferences());
simpleUserAttributes = getSimpleUserAttribute();
}

/**
Expand Down Expand Up @@ -99,7 +101,7 @@ public void deleteGlobalAttribute(String name) {
*/
public void addUserAttribute(String name, Object value) {
if (value != null) {
Event.EventError error = Event.checkUserAttribute(userAttributes.length(), name, value);
Event.EventError error = Event.checkUserAttribute(allUserAttributes.length(), name, value);
if (error != null) {
final AnalyticsEvent event = createEvent(Event.PresetEvent.CLICKSTREAM_ERROR);
event.addAttribute(Event.ReservedAttribute.ERROR_CODE, error.getErrorCode());
Expand All @@ -112,12 +114,12 @@ public void addUserAttribute(String name, Object value) {
JSONObject attribute = new JSONObject();
attribute.put("value", value);
attribute.put("set_timestamp", timeStamp);
userAttributes.put(name, attribute);
allUserAttributes.put(name, attribute);
} catch (JSONException exception) {
LOG.error("format user attribute, error message:" + exception.getMessage());
}
} else {
userAttributes.remove(name);
allUserAttributes.remove(name);
}
}

Expand All @@ -131,7 +133,7 @@ public void updateUserId(String userId) {
this.userId = userId;
PreferencesUtil.setCurrentUserId(context.getSystem().getPreferences(), userId);
if (!StringUtil.isNullOrEmpty(userId)) {
userAttributes = new JSONObject();
allUserAttributes = new JSONObject();
JSONObject userInfo = PreferencesUtil.getNewUserInfo(context.getSystem().getPreferences(), userId);
try {
userUniqueId = userInfo.getString("user_unique_id");
Expand All @@ -146,14 +148,15 @@ public void updateUserId(String userId) {
newUserId = null;
}
addUserAttribute(Event.ReservedAttribute.USER_ID, newUserId);
simpleUserAttributes = getSimpleUserAttribute();
}
}

/**
* update user attribute after user attribute changed.
*/
public void updateUserAttribute() {
PreferencesUtil.updateUserAttribute(context.getSystem().getPreferences(), userAttributes);
PreferencesUtil.updateUserAttribute(context.getSystem().getPreferences(), allUserAttributes);
}

/**
Expand Down Expand Up @@ -181,7 +184,10 @@ public AnalyticsEvent createEvent(String eventType) {

private AnalyticsEvent createAnalyticsEvent(String eventType) {
long timestamp = System.currentTimeMillis();
AnalyticsEvent event = new AnalyticsEvent(eventType, globalAttributes, userAttributes, timestamp, userUniqueId);
JSONObject eventUserAttribute =
eventType.equals(Event.PresetEvent.PROFILE_SET) ? allUserAttributes : simpleUserAttributes;
AnalyticsEvent event =
new AnalyticsEvent(eventType, globalAttributes, eventUserAttribute, timestamp, userUniqueId);
event.setDeviceId(this.context.getDeviceId());
event.setAppId(context.getClickstreamConfiguration().getAppId());
event.setSdkInfo(context.getSDKInfo());
Expand Down Expand Up @@ -235,4 +241,24 @@ public void setSession(Session session) {
public ClickstreamConfiguration getClickstreamConfiguration() {
return this.context.getClickstreamConfiguration();
}

/**
* get simple user attribute from allUserAttributes.
*
* @return userAttribute
*/
private JSONObject getSimpleUserAttribute() {
JSONObject userAttribute = new JSONObject();
try {
userAttribute.put(Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP,
allUserAttributes.getString(Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP));
if (allUserAttributes.has(Event.ReservedAttribute.USER_ID)) {
userAttribute.put(Event.ReservedAttribute.USER_ID,
allUserAttributes.getString(Event.ReservedAttribute.USER_ID));
}
} catch (final JSONException jsonException) {
LOG.error("Could not create Json object of simpleUserAttribute. error: " + jsonException.getMessage());
}
return userAttribute;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class AnalyticsClientTest {

private AnalyticsClient analyticsClient;
private Map<String, Object> globalAttributes;
private JSONObject userAttributes;
private JSONObject allUserAttributes;
private String exceedLengthName = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij";
private final String invalidName = "1_goods_expose";
private String exceedLengthValue = "";
Expand Down Expand Up @@ -78,7 +78,7 @@ public void setup() throws Exception {
analyticsClient = clickstreamManager.getAnalyticsClient();

globalAttributes = (Map<String, Object>) ReflectUtil.getFiled(analyticsClient, "globalAttributes");
userAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "userAttributes");
allUserAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "allUserAttributes");

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 21; i++) {
Expand Down Expand Up @@ -201,10 +201,10 @@ public void testAddGlobalAttributeSameNameMultiTimes() {
@Test
public void testAddUserAttributeWhenSuccess() throws JSONException {
analyticsClient.addUserAttribute("_user_age", 18);
Assert.assertTrue(userAttributes.has("_user_age"));
Assert.assertEquals(18, ((JSONObject) userAttributes.get("_user_age")).get("value"));
Assert.assertTrue(allUserAttributes.has("_user_age"));
Assert.assertEquals(18, ((JSONObject) allUserAttributes.get("_user_age")).get("value"));
Assert.assertTrue(System.currentTimeMillis() -
(Long) (((JSONObject) userAttributes.get("_user_age")).get("set_timestamp")) < 1000);
(Long) (((JSONObject) allUserAttributes.get("_user_age")).get("set_timestamp")) < 1000);
}

/**
Expand Down Expand Up @@ -266,8 +266,8 @@ public void testAddUserAttributeSameNameMultiTimes() throws JSONException {
mockAnalyticsClient.addUserAttribute("name", "value" + i);
}
verify(mockAnalyticsClient, never()).createEvent(anyString());
Assert.assertEquals(2, userAttributes.length());
Assert.assertEquals("value100", ((JSONObject) userAttributes.get("name")).get("value"));
Assert.assertEquals(2, allUserAttributes.length());
Assert.assertEquals("value100", ((JSONObject) allUserAttributes.get("name")).get("value"));
}

/**
Expand All @@ -287,9 +287,9 @@ public void testAddGlobalAttributeForNullValue() {
@Test
public void testAddUserAttributeForNullValue() {
analyticsClient.addUserAttribute("UserAge", 20);
Assert.assertTrue(userAttributes.has("UserAge"));
Assert.assertTrue(allUserAttributes.has("UserAge"));
analyticsClient.addUserAttribute("UserAge", null);
Assert.assertFalse(userAttributes.has("UserAge"));
Assert.assertFalse(allUserAttributes.has("UserAge"));
}

/**
Expand All @@ -316,10 +316,10 @@ public void testAddUserAttributeForNullValueWhenReachedMaxLength() {
for (int i = 0; i < 100; i++) {
analyticsClient.addUserAttribute("name" + i, "value" + i);
}
Assert.assertTrue(userAttributes.has("name0"));
Assert.assertTrue(allUserAttributes.has("name0"));
analyticsClient.addUserAttribute("name0", null);
Assert.assertFalse(userAttributes.has("name0"));
Assert.assertEquals(99, userAttributes.length());
Assert.assertFalse(allUserAttributes.has("name0"));
Assert.assertEquals(99, allUserAttributes.length());
}

/**
Expand All @@ -343,7 +343,7 @@ public void deleteNonExistingUserAttribute() {
analyticsClient.addUserAttribute("name" + i, "value" + i);
}
analyticsClient.addUserAttribute("name1000", null);
Assert.assertEquals(100, userAttributes.length());
Assert.assertEquals(100, allUserAttributes.length());
}

/**
Expand All @@ -365,7 +365,7 @@ public void testUserAttributeForStorage() throws Exception {
ClickstreamManagerFactory.create(context, AWSClickstreamPluginConfiguration
.builder().withAppId("demo-app").withEndpoint("http://example.com/collect").build());
JSONObject userAttributesFromStorage =
(JSONObject) ReflectUtil.getFiled(clickstreamManager.getAnalyticsClient(), "userAttributes");
(JSONObject) ReflectUtil.getFiled(clickstreamManager.getAnalyticsClient(), "allUserAttributes");
Assert.assertEquals(6, userAttributesFromStorage.length());
Assert.assertEquals("carl", ((JSONObject) userAttributesFromStorage.get("user_name")).getString("value"));
Assert.assertEquals("10837409", ((JSONObject) userAttributesFromStorage.get("_user_id")).getString("value"));
Expand All @@ -390,8 +390,8 @@ public void testInitialValueInAnalyticsClient() throws Exception {
Assert.assertEquals("", userId);
Assert.assertNotNull(userUniqueId);

userAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "userAttributes");
Assert.assertTrue(userAttributes.has(Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP));
allUserAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "allUserAttributes");
Assert.assertTrue(allUserAttributes.has(Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP));
}

/**
Expand All @@ -406,8 +406,8 @@ public void testUpdateSameUserIdTwice() throws Exception {
analyticsClient.updateUserId(userIdForA);
analyticsClient.addUserAttribute("user_age", 12);
analyticsClient.updateUserId(userIdForA);
userAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "userAttributes");
Assert.assertTrue(userAttributes.has("user_age"));
allUserAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "allUserAttributes");
Assert.assertTrue(allUserAttributes.has("user_age"));
Assert.assertEquals(userUniqueId, ReflectUtil.getFiled(analyticsClient, "userUniqueId"));
}

Expand All @@ -424,8 +424,8 @@ public void testUpdateDifferentUserId() throws Exception {
analyticsClient.updateUserId(userIdForA);
analyticsClient.addUserAttribute("user_age", 12);
analyticsClient.updateUserId(userIdForB);
userAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "userAttributes");
Assert.assertFalse(userAttributes.has("user_age"));
allUserAttributes = (JSONObject) ReflectUtil.getFiled(analyticsClient, "allUserAttributes");
Assert.assertFalse(allUserAttributes.has("user_age"));
Assert.assertNotEquals(userUniqueId, ReflectUtil.getFiled(analyticsClient, "userUniqueId"));
}

Expand All @@ -448,6 +448,26 @@ public void testChangeToOriginUserId() throws Exception {
Assert.assertEquals(userUniqueIdForB, ReflectUtil.getFiled(analyticsClient, "userUniqueId"));
}


/**
* test create event without custom user attributes.
*
* @throws JSONException the json exception
*/
@Test
public void testCreateEventWithoutCustomUserAttributes() throws JSONException {
analyticsClient.updateUserId("123");
analyticsClient.addUserAttribute("userName", "carl");
analyticsClient.addUserAttribute("userAge", 22);

AnalyticsEvent testEvent = analyticsClient.createEvent("testEvent");
JSONObject user = testEvent.toJSONObject().getJSONObject("user");
Assert.assertTrue(user.has(Event.ReservedAttribute.USER_ID));
Assert.assertTrue(user.has(Event.ReservedAttribute.USER_FIRST_TOUCH_TIMESTAMP));
Assert.assertFalse(user.has("userName"));
Assert.assertFalse(user.has("userAge"));
}

/**
* tearDown.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,21 +300,14 @@ public void testAddUserAttributes() throws Exception {
.add("_user_name", "carl")
.build();
ClickstreamAnalytics.addUserAttributes(clickstreamUserAttribute);
ClickstreamEvent event = ClickstreamEvent.builder()
.name("PasswordReset")
.add("Message", "SMS")
.add("Successful", true)
.add("ProcessDuration", 792)
.add("Number", 20.1)
.build();
ClickstreamAnalytics.recordEvent(event);
assertEquals(3, dbUtil.getTotalNumber());
assertEquals(2, dbUtil.getTotalNumber());

Cursor cursor = dbUtil.queryAllEvents();
cursor.moveToLast();
String eventString = cursor.getString(2);
JSONObject jsonObject = new JSONObject(eventString);
JSONObject attribute = jsonObject.getJSONObject("attributes");
String eventType = jsonObject.getString("event_type");
Assert.assertEquals(Event.PresetEvent.PROFILE_SET, eventType);
JSONObject user = jsonObject.getJSONObject("user");
Assert.assertEquals("13212", ((JSONObject) user.get(Event.ReservedAttribute.USER_ID)).getString("value"));
Assert.assertEquals(21, ((JSONObject) user.get("_user_age")).getInt("value"));
Expand All @@ -323,9 +316,6 @@ public void testAddUserAttributes() throws Exception {
Assert.assertTrue(((JSONObject) user.get("_user_name")).has("set_timestamp"));
Assert.assertEquals(timestamp, ((JSONObject) user.get("timestamp")).getLong("value"));

Assert.assertTrue(attribute.getBoolean("Successful"));
Assert.assertEquals("SMS", attribute.getString("Message"));

ClickstreamAnalytics.flushEvents();
Thread.sleep(1000);
assertEquals(0, dbUtil.getTotalNumber());
Expand All @@ -347,16 +337,8 @@ public void testModifyUserId() throws Exception {
.build();
ClickstreamAnalytics.setUserId("13212");
ClickstreamAnalytics.addUserAttributes(clickstreamUserAttribute);
ClickstreamEvent event = ClickstreamEvent.builder()
.name("PasswordReset")
.add("Message", "SMS")
.add("Successful", true)
.add("ProcessDuration", 792)
.add("Number", 20.1)
.build();
ClickstreamAnalytics.setUserId("12345");
ClickstreamAnalytics.recordEvent(event);
assertEquals(4, dbUtil.getTotalNumber());
assertEquals(3, dbUtil.getTotalNumber());

Cursor cursor = dbUtil.queryAllEvents();
cursor.moveToLast();
Expand Down Expand Up @@ -405,8 +387,8 @@ public void testSetUserIdNull() throws Exception {
JSONObject jsonObject = new JSONObject(eventString);
JSONObject user = jsonObject.getJSONObject("user");
Assert.assertFalse(user.has(Event.ReservedAttribute.USER_ID));
Assert.assertEquals(21, ((JSONObject) user.get("_user_age")).get("value"));
Assert.assertEquals("carl", ((JSONObject) user.get("_user_name")).get("value"));
Assert.assertFalse(user.has("_user_age"));
Assert.assertFalse(user.has("_user_name"));

ClickstreamAnalytics.flushEvents();
Thread.sleep(1000);
Expand Down Expand Up @@ -838,7 +820,8 @@ public void tearDown() throws Exception {
ClickstreamAnalytics.getClickStreamConfiguration().withCustomDns(null);
Map<String, Object> globalAttribute =
(Map<String, Object>) ReflectUtil.getFiled(analyticsClient, "globalAttributes");
ReflectUtil.modifyFiled(analyticsClient, "userAttributes", new JSONObject());
ReflectUtil.modifyFiled(analyticsClient, "simpleUserAttributes", new JSONObject());
ReflectUtil.modifyFiled(analyticsClient, "allUserAttributes", new JSONObject());
globalAttribute.clear();
}

Expand Down

0 comments on commit f43e9ca

Please sign in to comment.