Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing internal limits impl #304

Merged
merged 4 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ public void internalLimits_cancelTrace_keyLength() {
}

/**
* Test that tracing network keys are not affected by key length truncation
* Validate that the truncated version of the key is not present because it is not truncated
* Test that tracing network keys are affected by key length truncation
* Validate that the truncated version of the key is present because it is truncated
*/
@Test
public void internalLimits_recordNetworkTrace_keyLength() throws JSONException {
Expand All @@ -314,11 +314,11 @@ public void internalLimits_recordNetworkTrace_keyLength() throws JSONException {

mCountly.apm().recordNetworkTrace(key, 234, 123, 456, 7654, 8765);
Assert.assertFalse(mCountly.moduleAPM.networkTraces.containsKey(key)); // because it is sent to the request queue
Assert.assertFalse(mCountly.moduleAPM.networkTraces.containsKey("a_tra")); // because it is sent to the request queue

Assert.assertFalse(mCountly.moduleAPM.codeTraces.containsKey(key));
// also validate that the truncated version of the key is not present because it is not truncated
Assert.assertFalse(mCountly.moduleAPM.codeTraces.containsKey(UtilsInternalLimits.truncateKeyLength(key, 5, new ModuleLog(), "tag")));
validateNetworkRequest(0, key, 8765 - 7654, 234, 123, 456);
Assert.assertFalse(mCountly.moduleAPM.codeTraces.containsKey("a_tra"));
validateNetworkRequest(0, "a_tra", 8765 - 7654, 234, 123, 456);
}

/**
Expand All @@ -336,7 +336,7 @@ public void internalLimits_startNetworkTrace_keyLength() throws JSONException {
mCountly.apm().startNetworkRequest(key, "ID");
mCountly.apm().endNetworkRequest(key, "ID", 200, 123, 456);

validateNetworkRequest(0, key, -1, 200, 123, 456);
validateNetworkRequest(0, "a_tra", -1, 200, 123, 456);
}

private void validateNetworkRequest(int rqIdx, String key, long duration, int responseCode, int requestPayloadSize, int responsePayloadSize) throws JSONException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ public void internalLimit_setProperties() throws JSONException {
/**
* Given max value size truncates the values of the:
* - Custom user property values
* - user property values
* - user property values except picture
* Validate all values are truncated to the max value size that is 2
* And validate non-String values are not clipped
*
Expand Down Expand Up @@ -576,7 +576,7 @@ public void internalLimit_setProperties_maxValueSize() throws JSONException {
ModuleUserProfile.ORG_KEY, "or",
ModuleUserProfile.USERNAME_KEY, "us",
ModuleUserProfile.NAME_KEY, "na",
ModuleUserProfile.PICTURE_KEY, "pi"
ModuleUserProfile.PICTURE_KEY, "picture"
), TestUtils.map(
"custom1", "va", // because in user profiles, all values are stored as strings
"custom2", "23",
Expand All @@ -588,6 +588,67 @@ public void internalLimit_setProperties_maxValueSize() throws JSONException {
);
}

/**
* Given max value size for pictures 4096 truncates the value of the picture
* and is not affected by the general max value size
*
* @throws JSONException if JSON parsing fails
*/
@Test
public void internalLimit_setProperties_maxValueSizePicture() throws JSONException {
Countly mCountly = Countly.sharedInstance();
CountlyConfig config = TestUtils.createBaseConfig();
config.sdkInternalLimits.setMaxValueSize(2);
mCountly.init(config);

String picture = TestUtils.generateRandomString(6000);

Countly.sharedInstance().userProfile().setProperties(TestUtils.map(ModuleUserProfile.PICTURE_KEY, picture));
Countly.sharedInstance().userProfile().save();

validateUserProfileRequest(TestUtils.map(ModuleUserProfile.PICTURE_KEY, picture.substring(0, 4096)), TestUtils.map()
);
}

/**
* Given max segmentation values will truncate custom user properties to the correct length
*
* @throws JSONException if JSON parsing fails
*/
@Test
public void internalLimit_setProperties_maxSegmentationValues() throws JSONException {
Countly mCountly = Countly.sharedInstance();
CountlyConfig config = TestUtils.createBaseConfig();
config.sdkInternalLimits.setMaxSegmentationValues(2);
mCountly.init(config);

Countly.sharedInstance().userProfile().setProperties(TestUtils.map("a", "b", "c", "d", "f", 5, "level", 45, "age", 101));
Countly.sharedInstance().userProfile().save();

validateUserProfileRequest(TestUtils.map(), TestUtils.map("f", "5", "age", "101"));
}

/**
* Given max segmentation values won't truncate custom mods
*
* @throws JSONException if JSON parsing fails
*/
@Test
public void internalLimit_customMods_maxSegmentationValues() throws JSONException {
Countly mCountly = Countly.sharedInstance();
CountlyConfig config = TestUtils.createBaseConfig();
config.sdkInternalLimits.setMaxSegmentationValues(2);
mCountly.init(config);

Countly.sharedInstance().userProfile().incrementBy("inc", 1);
Countly.sharedInstance().userProfile().multiply("mul", 2_456_789);
Countly.sharedInstance().userProfile().push("rem", "ORIELY");
Countly.sharedInstance().userProfile().push("rem", "HUH");
Countly.sharedInstance().userProfile().save();

validateUserProfileRequest(TestUtils.map(), TestUtils.map("mul", json("$mul", 2_456_789), "rem", json("$push", new String[] { "ORIELY", "HUH" }), "inc", json("$inc", 1)));
}

/**
* Given max value size truncates the values of the:
* - Custom user property values
Expand Down Expand Up @@ -645,6 +706,14 @@ public void setUserProperties_null() throws JSONException {
validateUserProfileRequest(new HashMap<>(), new HashMap<>());
}

private JSONObject json(Object... args) {
return new JSONObject(TestUtils.map(args));
}

private void assertJsonsEquals(Object expected, Object actual) {
Assert.assertEquals(expected.toString(), actual.toString());
}

private void validateUserProfileRequest(Map<String, Object> predefined, Map<String, Object> custom) throws JSONException {
Map<String, String>[] RQ = TestUtils.getCurrentRQ();
Assert.assertEquals(1, RQ.length);
Expand All @@ -658,7 +727,11 @@ private void validateUserProfileRequest(Map<String, Object> predefined, Map<Stri
}

for (Map.Entry<String, Object> entry : custom.entrySet()) {
Assert.assertEquals(entry.getValue(), customData.get(entry.getKey()));
if (entry.getValue() instanceof JSONObject) {
assertJsonsEquals(entry.getValue(), customData.get(entry.getKey()));
} else {
Assert.assertEquals(entry.getValue(), customData.get(entry.getKey()));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -568,4 +568,11 @@ protected static void put(JSONObject json, String key, Object value) {
} catch (JSONException ignored) {
}
}

protected static String generateRandomString(int length) {
byte[] array = new byte[length];
new Random().nextBytes(array);

return new String(array, java.nio.charset.StandardCharsets.UTF_8);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public class ConfigSdkInternalLimits {
//SDK internal limits
protected Integer maxKeyLength;
protected Integer maxValueSize;
protected int maxValueSizePicture = 4096;
protected Integer maxSegmentationValues;
protected Integer maxBreadcrumbCount;
protected Integer maxStackTraceLinesPerThread;
Expand Down
1 change: 1 addition & 0 deletions sdk/src/main/java/ly/count/android/sdk/ModuleAPM.java
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ void recordNetworkRequestInternal(String networkTraceKey, int responseCode, int
}

//validate trace key
networkTraceKey = UtilsInternalLimits.truncateKeyLength(networkTraceKey, _cly.config_.sdkInternalLimits.maxKeyLength, L, "[ModuleAPM] recordNetworkRequestInternal");
networkTraceKey = validateAndModifyTraceKey(networkTraceKey);

Long responseTimeMs = endTimestamp - startTimestamp;
Expand Down
10 changes: 8 additions & 2 deletions sdk/src/main/java/ly/count/android/sdk/ModuleUserProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ protected JSONObject toJSON() {

JSONObject ob;
if (custom != null) {
UtilsInternalLimits.truncateSegmentationValues(custom, _cly.config_.sdkInternalLimits.maxSegmentationValues, "[ModuleUserProfile] toJSON", _cly.L);
ob = new JSONObject(custom);
} else {
ob = new JSONObject();
Expand Down Expand Up @@ -235,6 +236,7 @@ void modifyCustomData(String key, Object value, String mod) {
if (customMods == null) {
customMods = new HashMap<>();
}

JSONObject ob;
if (!mod.equals("$pull") && !mod.equals("$push") && !mod.equals("$addToSet")) {
ob = new JSONObject();
Expand Down Expand Up @@ -282,8 +284,12 @@ void setPropertiesInternal(@NonNull Map<String, Object> data) {
boolean isNamed = false;

// limit to the picture path is applied when request is being made in the ConnectionProcessor
if (value instanceof String && !key.equals(PICTURE_PATH_KEY)) {
value = UtilsInternalLimits.truncateValueSize(value.toString(), _cly.config_.sdkInternalLimits.maxValueSize, _cly.L, "[ModuleUserProfile] setPropertiesInternal");
if (value instanceof String) {
if (key.equals(PICTURE_PATH_KEY) || key.equals(PICTURE_KEY)) {
value = UtilsInternalLimits.truncateValueSize(value.toString(), _cly.config_.sdkInternalLimits.maxValueSizePicture, _cly.L, "[ModuleUserProfile] setPropertiesInternal");
} else {
value = UtilsInternalLimits.truncateValueSize(value.toString(), _cly.config_.sdkInternalLimits.maxValueSize, _cly.L, "[ModuleUserProfile] setPropertiesInternal");
}
}

for (String namedField : namedFields) {
Expand Down