From 80d1bc1769858907003ccefe230854931638743d Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 10:59:29 +0900 Subject: [PATCH 01/34] chore(sdk): add constant values for collect custom attributes --- .../deploygate/service/DeployGateEvent.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index 8b12a57..540f40b 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -8,6 +8,7 @@ public interface DeployGateEvent { // namespace: // ACTION => a // EXTRA => e + // KEY => k // // content: // should be hyphen-separated string and be lower cases @@ -27,6 +28,11 @@ public interface DeployGateEvent { public static final String ACTION_COMPOSE_COMMENT = "composeComment"; public static final String ACTION_VISIBILITY_EVENT = "a.visibility-event"; + /** + * @since 4.8.0 + */ + public static final String ACTION_COLLECT_DEVICE_STATES = "a.collect-device-states"; + public static final String EXTRA_AUTHOR = "author"; public static final String EXTRA_EXPECTED_AUTHOR = "expectedAuthor"; @@ -121,6 +127,31 @@ public interface DeployGateEvent { */ public static final String EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS = "e.visibility-event-elapsed-real-time"; + /** + * @since 4.8.0 + */ + public static final String EXTRA_TARGET_URI_FOR_REPORT_DEVICE_STATES = "e.target-uri-for-report-device-states"; + + /** + * @since 4.8.0 + */ + public static final String KEY_BUILD_ENVIRONMENT = "k.build-environment"; + + /** + * @since 4.8.0 + */ + public static final String KEY_RUNTIME_EXTRAS = "k.runtime-extras"; + + /** + * @since 4.8.0 + */ + public static final String KEY_PACKAGE_NAME = "k.package-name"; + + /** + * @since 4.8.0 + */ + public static final String KEY_EVENT_AT = "k.event-at"; + interface VisibilityType { int BACKGROUND = 0; int FOREGROUND = 1; From 6be47564190897012722057c8e2816912a104f2e Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 11:08:57 +0900 Subject: [PATCH 02/34] feat(sdk): add interface for set/remove custom attributes from user --- .../java/com/deploygate/sdk/DeployGate.java | 136 ++++++++++++++++++ .../sdk/DeployGateInterfaceTest.java | 70 +++++++++ .../java/com/deploygate/sdk/DeployGate.java | 60 ++++++++ 3 files changed, 266 insertions(+) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index a9e60a7..139992a 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -1390,4 +1390,140 @@ public static String getDistributionUserName() { return sInstance.mDistributionUserName; } + + public static boolean putBuildEnvironmentValue(String key, String value) { + if (sInstance != null) { + return sInstance.putBuildEnvironmentValueInternal(key, value); + } + return false; + } + + public static boolean putBuildEnvironmentValue(String key, int value) { + if (sInstance != null) { + return sInstance.putBuildEnvironmentValueInternal(key, value); + } + return false; + } + + public static boolean putBuildEnvironmentValue(String key, long value) { + if (sInstance != null) { + return sInstance.putBuildEnvironmentValueInternal(key, value); + } + return false; + } + + public static boolean putBuildEnvironmentValue(String key, float value) { + if (sInstance != null) { + return sInstance.putBuildEnvironmentValueInternal(key, value); + } + return false; + } + + public static boolean putBuildEnvironmentValue(String key, double value) { + if (sInstance != null) { + return sInstance.putBuildEnvironmentValueInternal(key, value); + } + return false; + } + + public static boolean putBuildEnvironmentValue(String key, boolean value) { + if (sInstance != null) { + return sInstance.putBuildEnvironmentValueInternal(key, value); + } + return false; + } + + public static void removeBuildEnvironmentValue(String key) { + if (sInstance != null) { + sInstance.removeBuildEnvironmentValueInternal(key); + } + } + + public static void removeAllBuildEnvironmentValues() { + if (sInstance != null) { + sInstance.removeAllBuildEnvironmentValuesInternal(); + } + } + + public static boolean putRuntimeExtraValue(String key, String value) { + if (sInstance != null) { + return sInstance.putRuntimeExtraValueInternal(key, value); + } + return false; + } + + public static boolean putRuntimeExtraValue(String key, int value) { + if (sInstance != null) { + return sInstance.putRuntimeExtraValueInternal(key, value); + } + return false; + } + + public static boolean putRuntimeExtraValue(String key, long value) { + if (sInstance != null) { + return sInstance.putRuntimeExtraValueInternal(key, value); + } + return false; + } + + public static boolean putRuntimeExtraValue(String key, float value) { + if (sInstance != null) { + return sInstance.putRuntimeExtraValueInternal(key, value); + } + return false; + } + + public static boolean putRuntimeExtraValue(String key, double value) { + if (sInstance != null) { + return sInstance.putRuntimeExtraValueInternal(key, value); + } + return false; + } + + public static boolean putRuntimeExtraValue(String key, boolean value) { + if (sInstance != null) { + return sInstance.putRuntimeExtraValueInternal(key, value); + } + return false; + } + + public static void removeRuntimeExtraValue(String key) { + if (sInstance != null) { + sInstance.removeRuntimeExtraValueInternal(key); + } + } + + public static void removeAllRuntimeExtraValues() { + if (sInstance != null) { + sInstance.removeAllRuntimeExtraValuesInternal(); + } + } + + private boolean putBuildEnvironmentValueInternal(String key, Object value) { + return true; + } + + private void removeBuildEnvironmentValueInternal(String key) { + } + + private void removeAllBuildEnvironmentValuesInternal() { + } + + private boolean putRuntimeExtraValueInternal(String key, Object value) { + return true; + } + + private void removeRuntimeExtraValueInternal(String key) { + } + + private void removeAllRuntimeExtraValuesInternal() { + } + + private boolean isValidKey(String key) { + return true; + } + + private boolean isValidValue(Object value) { + return true; + } } diff --git a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java index 90622bc..f0fbe57 100644 --- a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java @@ -364,4 +364,74 @@ public void composeComment__String() { public void getDistributionUserName() { Truth.assertThat(DeployGate.getDistributionUserName()).isNull(); } + + @Test + public void putBuildEnvironmentValue__string() { + DeployGate.putBuildEnvironmentValue("key", "value"); + } + + @Test + public void putBuildEnvironmentValue__int() { + DeployGate.putBuildEnvironmentValue("key", 1); + } + + @Test + public void putBuildEnvironmentValue__float() { + DeployGate.putBuildEnvironmentValue("key", 1.0f); + } + + @Test + public void putBuildEnvironmentValue__double() { + DeployGate.putBuildEnvironmentValue("key", 1.0); + } + + @Test + public void putBuildEnvironmentValue__long() { + DeployGate.putBuildEnvironmentValue("key", 1L); + } + + @Test + public void removeBuildEnvironmentValue() { + DeployGate.removeBuildEnvironmentValue("key"); + } + + @Test + public void removeAllBuildEnvironmentValues() { + DeployGate.removeAllBuildEnvironmentValues(); + } + + @Test + public void putRuntimeExtraValue__string() { + DeployGate.putRuntimeExtraValue("key", "value"); + } + + @Test + public void putRuntimeExtrasValue__int() { + DeployGate.putRuntimeExtraValue("key", 1); + } + + @Test + public void putRuntimeExtrasValue__float() { + DeployGate.putRuntimeExtraValue("key", 1.0f); + } + + @Test + public void putRuntimeExtrasValue__double() { + DeployGate.putRuntimeExtraValue("key", 1.0); + } + + @Test + public void putRuntimeExtrasValue__long() { + DeployGate.putRuntimeExtraValue("key", 1L); + } + + @Test + public void removeRuntimeExtrasValue() { + DeployGate.removeRuntimeExtraValue("key"); + } + + @Test + public void removeAllRuntimeExtraValues() { + DeployGate.removeAllRuntimeExtraValues(); + } } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java index 22e096f..0df1695 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java @@ -179,4 +179,64 @@ public static void composeComment(String defaultComment) { public static String getDistributionUserName() { return null; } + + public static boolean putBuildEnvironmentValue(String key, String value) { + return false; + } + + public static boolean putBuildEnvironmentValue(String key, int value) { + return false; + } + + public static boolean putBuildEnvironmentValue(String key, long value) { + return false; + } + + public static boolean putBuildEnvironmentValue(String key, float value) { + return false; + } + + public static boolean putBuildEnvironmentValue(String key, double value) { + return false; + } + + public static boolean putBuildEnvironmentValue(String key, boolean value) { + return false; + } + + public static void removeBuildEnvironmentValue(String key) { + } + + public static void removeAllBuildEnvironmentValues() { + } + + public static boolean putRuntimeExtrasValue(String key, String value) { + return false; + } + + public static boolean putRuntimeExtrasValue(String key, int value) { + return false; + } + + public static boolean putRuntimeExtrasValue(String key, long value) { + return false; + } + + public static boolean putRuntimeExtrasValue(String key, float value) { + return false; + } + + public static boolean putRuntimeExtrasValue(String key, double value) { + return false; + } + + public static boolean putRuntimeExtrasValue(String key, boolean value) { + return false; + } + + public static void removeRuntimeExtrasValue(String key) { + } + + public static void removeAllRuntimeExtrasValues() { + } } From 58aa86589ff675022bdc1f884e137a4317ae5739 Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 11:11:30 +0900 Subject: [PATCH 03/34] feat(sdk): manage custom attributes with HashMap and implement validation --- .../java/com/deploygate/sdk/DeployGate.java | 63 +++++++ .../sdk/DeployGateInterfaceTest.java | 157 ++++++++++++++++++ 2 files changed, 220 insertions(+) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 139992a..c7a3245 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -49,6 +49,13 @@ public class DeployGate { private static final String DEPLOYGATE_PACKAGE = "com.deploygate"; private static final Object sPendingEventLock = new Object(); + private static final int MAX_BUILD_ENVIRONMENTS_SIZE = 8; + private static final int MAX_RUNTIME_EXTRAS_SIZE = 8; + private static final Pattern USER_INPUT_KEY_PATTERN = Pattern.compile("^[a-z][_a-z0-9]{2,31}$"); + private static final int MIN_USER_INPUT_KEY_LENGTH = 3; + private static final int MAX_USER_INPUT_KEY_LENGTH = 32; + private static final int MAX_USER_INPUT_VALUE_LENGTH = 64; + private static DeployGate sInstance; private final Context mApplicationContext; @@ -59,6 +66,8 @@ public class DeployGate { private final CustomLogInstructionSerializer mCustomLogInstructionSerializer; private final HashSet mCallbacks; private final HashMap mPendingEvents; + private final HashMap mBuildEnvironments; + private final HashMap mRuntimeExtras; private final String mExpectedAuthor; private String mAuthor; @@ -279,6 +288,8 @@ private DeployGate( mCustomLogInstructionSerializer = new CustomLogInstructionSerializer(mHostApp.packageName, sdkConfiguration.customLogConfiguration); mCallbacks = new HashSet<>(); mPendingEvents = new HashMap<>(); + mBuildEnvironments = new HashMap<>(); + mRuntimeExtras = new HashMap<>(); mExpectedAuthor = sdkConfiguration.appOwnerName; prepareBroadcastReceiver(); @@ -1500,30 +1511,82 @@ public static void removeAllRuntimeExtraValues() { } private boolean putBuildEnvironmentValueInternal(String key, Object value) { + if (mBuildEnvironments.size() >= MAX_BUILD_ENVIRONMENTS_SIZE && !mBuildEnvironments.containsKey(key)) { + Log.w(TAG, "BuildEnvironment already full. Ignored: " + key); + return false; + } + + if (!isValidKey(key)) { + return false; + } + + if (!isValidValue(value)) { + return false; + } + + mBuildEnvironments.put(key, value); return true; } private void removeBuildEnvironmentValueInternal(String key) { + mBuildEnvironments.remove(key); } private void removeAllBuildEnvironmentValuesInternal() { + mBuildEnvironments.clear(); } private boolean putRuntimeExtraValueInternal(String key, Object value) { + if (mRuntimeExtras.size() >= MAX_RUNTIME_EXTRAS_SIZE && !mRuntimeExtras.containsKey(key)) { + Log.w(TAG, "RuntimeExtra already full. Ignored: " + key); + return false; + } + + if (!isValidKey(key)) { + return false; + } + + if (!isValidValue(value)) { + return false; + } + + mRuntimeExtras.put(key, value); return true; } private void removeRuntimeExtraValueInternal(String key) { + mRuntimeExtras.remove(key); } private void removeAllRuntimeExtraValuesInternal() { + mRuntimeExtras.clear(); } private boolean isValidKey(String key) { + if (key == null || key.equals("true") || key.equals("false") || key.equals("null")) { + Log.w(TAG, "Not allowed key: " + key); + return false; + } + + if (key.length() < MIN_USER_INPUT_KEY_LENGTH || key.length() > MAX_USER_INPUT_KEY_LENGTH || !USER_INPUT_KEY_PATTERN.matcher(key).matches()) { + Log.w(TAG, "Invalid key: " + key); + return false; + } + return true; } private boolean isValidValue(Object value) { + if (value == null) { + Log.w(TAG, "Value is null"); + return false; + } + + if (value instanceof String && ((String) value).length() > MAX_USER_INPUT_VALUE_LENGTH) { + Log.w(TAG, "Value too long: " + value); + return false; + } + return true; } } diff --git a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java index f0fbe57..97ccbbf 100644 --- a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java @@ -434,4 +434,161 @@ public void removeRuntimeExtrasValue() { public void removeAllRuntimeExtraValues() { DeployGate.removeAllRuntimeExtraValues(); } + + @Test + public void putCustomValues() { + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid", "value")).isFalse(); + + DeployGate.install(app); + + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid", "value")).isTrue(); + } + + @Test + public void putCustomValues__keyPattern() { + DeployGate.install(app); + + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_underscore", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_1_number", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("min", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("ng", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("true", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("false", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("null", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid-hyphen", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid#sharp", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid$dollar", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid.dot", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid!bang", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid*glob", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalidUpperCase", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("12345", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("1_invalid_begin_number", "value")).isFalse(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_key_with_length_under_32", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid_key_with_length_over_32_characters", "value")).isFalse(); + + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_underscore", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_1_number", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("min", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("ng", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("true", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("false", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("null", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid-hyphen", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid#sharp", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid$dollar", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid.dot", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid!bang", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid*glob", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalidUpperCase", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("12345", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("1_invalid_begin_number", "value")).isFalse(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_key_with_length_under_32", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid_key_with_length_over_32_characters", "value")).isFalse(); + } + + @Test + public void putCustomValues__valuePattern() { + DeployGate.install(app); + + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_string", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_int", 1)).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_long", 1L)).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_float", 1.1f)).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_double", 1.1)).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_boolean", true)).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid_too_long_string", "this is too long string value. we cannot accept value if size over 64.")).isFalse(); + + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_string", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_int", 1)).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_long", 1L)).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_float", 1.1f)).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_double", 1.1)).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_boolean", true)).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid_too_long_string", "this is too long string value. we cannot accept value if size over 64.")).isFalse(); + } + + @Test + public void setBuildEnvironment__maxSize() { + DeployGate.install(app); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key2", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key3", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key4", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key5", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key6", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key7", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key8", "value")).isTrue(); + + // allow to overwrite + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value2")).isTrue(); + // not allow to put value with new key because of max size + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key9", "value")).isFalse(); + + DeployGate.removeBuildEnvironmentValue("key8"); + + // allow to put value with new key after remove exists key + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key9", "value")).isTrue(); + // allow to overwrite + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value3")).isTrue(); + // not allow to put value with new key because of max size + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key10", "value")).isFalse(); + + DeployGate.removeAllBuildEnvironmentValues(); + + // allow to put value less than max size + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key2", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key3", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key4", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key5", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key6", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key7", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key8", "value")).isTrue(); + Truth.assertThat(DeployGate.putBuildEnvironmentValue("key9", "value")).isFalse(); + } + + @Test + public void setRuntimeExtra__maxSize() { + DeployGate.install(app); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key2", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key3", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key4", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key5", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key6", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key7", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key8", "value")).isTrue(); + + // allow to overwrite + Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value2")).isTrue(); + // not allow to put value with new key because of max size + Truth.assertThat(DeployGate.putRuntimeExtraValue("key9", "value")).isFalse(); + + DeployGate.removeRuntimeExtraValue("key8"); + + // allow to put value with new key after remove exists key + Truth.assertThat(DeployGate.putRuntimeExtraValue("key9", "value")).isTrue(); + // allow to overwrite + Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value3")).isTrue(); + // not allow to put value with new key because of max size + Truth.assertThat(DeployGate.putRuntimeExtraValue("key10", "value")).isFalse(); + + DeployGate.removeAllRuntimeExtraValues(); + + // allow to put value less than max size + Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key2", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key3", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key4", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key5", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key6", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key7", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key8", "value")).isTrue(); + Truth.assertThat(DeployGate.putRuntimeExtraValue("key9", "value")).isFalse(); + } } From fd547511abb22c160dd84acceea6a98409781cb1 Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 11:27:20 +0900 Subject: [PATCH 04/34] chore(sdk): bumpup version to 4.8.0 --- .../src/main/java/com/deploygate/plugins/BaseSdkPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/library/src/main/java/com/deploygate/plugins/BaseSdkPlugin.kt b/plugins/library/src/main/java/com/deploygate/plugins/BaseSdkPlugin.kt index 2e072ea..fe1b960 100644 --- a/plugins/library/src/main/java/com/deploygate/plugins/BaseSdkPlugin.kt +++ b/plugins/library/src/main/java/com/deploygate/plugins/BaseSdkPlugin.kt @@ -16,7 +16,7 @@ abstract class BaseSdkPlugin : Plugin { /** * sdk/java/com/deploygate/sdk/HostAppTest.java needs to be changed for a new release */ - const val ARTIFACT_VERSION = "4.7.1" + const val ARTIFACT_VERSION = "4.8.0" val JAVA_VERSION = JavaVersion.VERSION_1_7 } From e4a3d4017e061254af83eb8bdfce18c5db5854a3 Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 11:49:12 +0900 Subject: [PATCH 05/34] fix(sdk): add import --- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index c7a3245..1a2c239 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; /** * This is DeployGate SDK library. Import this library to the application From ce47071306258be66973068eeb75b784d6c25ad3 Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 15:33:45 +0900 Subject: [PATCH 06/34] fix(sdk): fix test failed --- .../java/com/deploygate/sdk/HostAppTest.java | 2 +- .../main/java/com/deploygate/sdk/DeployGate.java | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java b/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java index a4f2999..5793449 100644 --- a/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java @@ -36,7 +36,7 @@ public void default_properties() { Truth.assertThat(app.canUseLogcat).isTrue(); Truth.assertThat(app.packageName).isEqualTo("com.deploygate.sdk.test"); Truth.assertThat(app.sdkVersion).isEqualTo(4); - Truth.assertThat(app.sdkArtifactVersion).isEqualTo("4.7.1"); + Truth.assertThat(app.sdkArtifactVersion).isEqualTo("4.8.0"); Truth.assertThat(app.activeFeatureFlags).isEqualTo(FULL_BIT); Truth.assertThat(app.canUseDeviceCapture()).isTrue(); } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java index 0df1695..963d408 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java @@ -210,33 +210,33 @@ public static void removeBuildEnvironmentValue(String key) { public static void removeAllBuildEnvironmentValues() { } - public static boolean putRuntimeExtrasValue(String key, String value) { + public static boolean putRuntimeExtraValue(String key, String value) { return false; } - public static boolean putRuntimeExtrasValue(String key, int value) { + public static boolean putRuntimeExtraValue(String key, int value) { return false; } - public static boolean putRuntimeExtrasValue(String key, long value) { + public static boolean putRuntimeExtraValue(String key, long value) { return false; } - public static boolean putRuntimeExtrasValue(String key, float value) { + public static boolean putRuntimeExtraValue(String key, float value) { return false; } - public static boolean putRuntimeExtrasValue(String key, double value) { + public static boolean putRuntimeExtraValue(String key, double value) { return false; } - public static boolean putRuntimeExtrasValue(String key, boolean value) { + public static boolean putRuntimeExtraValue(String key, boolean value) { return false; } - public static void removeRuntimeExtrasValue(String key) { + public static void removeRuntimeExtraValue(String key) { } - public static void removeAllRuntimeExtrasValues() { + public static void removeAllRuntimeExtraValues() { } } From b94250d009624ce204ca5900fd8d03a6a40495bb Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 18:05:02 +0900 Subject: [PATCH 07/34] test(sdk): add test for CustomAttributes --- .../deploygate/sdk/CustomAttributesTest.java | 97 ++++++++ .../sdk/DeployGateInterfaceTest.java | 221 +----------------- 2 files changed, 109 insertions(+), 209 deletions(-) create mode 100644 sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java new file mode 100644 index 0000000..6350857 --- /dev/null +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -0,0 +1,97 @@ +package com.deploygate.sdk; + +import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.google.common.truth.Truth; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CustomAttributesTest { + + @NonNull + CustomAttributes attributes; + + @Before + public void setUp() { + attributes = new CustomAttributes(); + } + + @Test + public void put__key_pattern() { + Truth.assertThat(attributes.putString("valid", "value")).isTrue(); + Truth.assertThat(attributes.putString("valid_underscore", "value")).isTrue(); + Truth.assertThat(attributes.putString("valid_1_number", "value")).isTrue(); + Truth.assertThat(attributes.putString("min", "value")).isTrue(); + Truth.assertThat(attributes.putString("valid_key_with_length_under_32", "value")).isTrue(); + + Truth.assertThat(attributes.putString("ng", "value")).isFalse(); + Truth.assertThat(attributes.putString("true", "value")).isFalse(); + Truth.assertThat(attributes.putString("false", "value")).isFalse(); + Truth.assertThat(attributes.putString("null", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalid-hyphen", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalid#sharp", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalid$dollar", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalid.dot", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalid!bang", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalid*glob", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalidUpperCase", "value")).isFalse(); + Truth.assertThat(attributes.putString("12345", "value")).isFalse(); + Truth.assertThat(attributes.putString("1_invalid_begin_number", "value")).isFalse(); + Truth.assertThat(attributes.putString("invalid_key_with_length_over_32_characters", "value")).isFalse(); + } + + @Test + public void put__value_pattern() { + Truth.assertThat(attributes.putString("valid_string", "value")).isTrue(); + Truth.assertThat(attributes.putInt("valid_int", 1)).isTrue(); + Truth.assertThat(attributes.putLong("valid_long", 1L)).isTrue(); + Truth.assertThat(attributes.putFloat("valid_float", 1.1f)).isTrue(); + Truth.assertThat(attributes.putDouble("valid_double", 1.1)).isTrue(); + Truth.assertThat(attributes.putBoolean("valid_boolean", true)).isTrue(); + + Truth.assertThat(attributes.putString("invalid_too_long_string", "this is too long string value. we cannot accept value if size over 64.")).isFalse(); + } + + @Test + public void put__max_size() { + Truth.assertThat(attributes.putString("key1", "value")).isTrue(); + Truth.assertThat(attributes.putString("key2", "value")).isTrue(); + Truth.assertThat(attributes.putString("key3", "value")).isTrue(); + Truth.assertThat(attributes.putString("key4", "value")).isTrue(); + Truth.assertThat(attributes.putString("key5", "value")).isTrue(); + Truth.assertThat(attributes.putString("key6", "value")).isTrue(); + Truth.assertThat(attributes.putString("key7", "value")).isTrue(); + Truth.assertThat(attributes.putString("key8", "value")).isTrue(); + + // allow to overwrite + Truth.assertThat(attributes.putString("key1", "value2")).isTrue(); + // not allow to put value with new key because of max size + Truth.assertThat(attributes.putString("key9", "value")).isFalse(); + + attributes.remove("key8"); + + // allow to put value with new key after remove exists key + Truth.assertThat(attributes.putString("key9", "value")).isTrue(); + // allow to overwrite + Truth.assertThat(attributes.putString("key1", "value3")).isTrue(); + // not allow to put value with new key because of max size + Truth.assertThat(attributes.putString("key10", "value")).isFalse(); + + attributes.removeAll(); + + // allow to put value less than max size + Truth.assertThat(attributes.putString("key1", "value")).isTrue(); + Truth.assertThat(attributes.putString("key2", "value")).isTrue(); + Truth.assertThat(attributes.putString("key3", "value")).isTrue(); + Truth.assertThat(attributes.putString("key4", "value")).isTrue(); + Truth.assertThat(attributes.putString("key5", "value")).isTrue(); + Truth.assertThat(attributes.putString("key6", "value")).isTrue(); + Truth.assertThat(attributes.putString("key7", "value")).isTrue(); + Truth.assertThat(attributes.putString("key8", "value")).isTrue(); + Truth.assertThat(attributes.putString("key9", "value")).isFalse(); + } +} diff --git a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java index 97ccbbf..d7570d1 100644 --- a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java @@ -366,229 +366,32 @@ public void getDistributionUserName() { } @Test - public void putBuildEnvironmentValue__string() { - DeployGate.putBuildEnvironmentValue("key", "value"); + public void getBuildEnvironment() { + Truth.assertThat(DeployGate.getBuildEnvironment()).isNull(); } @Test - public void putBuildEnvironmentValue__int() { - DeployGate.putBuildEnvironmentValue("key", 1); + public void setBuildEnvironment() { + DeployGate.setBuildEnvironment(null); } @Test - public void putBuildEnvironmentValue__float() { - DeployGate.putBuildEnvironmentValue("key", 1.0f); + public void clearBuildEnvironment() { + DeployGate.clearBuildEnvironment(); } @Test - public void putBuildEnvironmentValue__double() { - DeployGate.putBuildEnvironmentValue("key", 1.0); + public void getRuntimeExtra() { + Truth.assertThat(DeployGate.getRuntimeExtra()).isNull(); } @Test - public void putBuildEnvironmentValue__long() { - DeployGate.putBuildEnvironmentValue("key", 1L); + public void setRuntimeExtra() { + DeployGate.setRuntimeExtra(null); } @Test - public void removeBuildEnvironmentValue() { - DeployGate.removeBuildEnvironmentValue("key"); - } - - @Test - public void removeAllBuildEnvironmentValues() { - DeployGate.removeAllBuildEnvironmentValues(); - } - - @Test - public void putRuntimeExtraValue__string() { - DeployGate.putRuntimeExtraValue("key", "value"); - } - - @Test - public void putRuntimeExtrasValue__int() { - DeployGate.putRuntimeExtraValue("key", 1); - } - - @Test - public void putRuntimeExtrasValue__float() { - DeployGate.putRuntimeExtraValue("key", 1.0f); - } - - @Test - public void putRuntimeExtrasValue__double() { - DeployGate.putRuntimeExtraValue("key", 1.0); - } - - @Test - public void putRuntimeExtrasValue__long() { - DeployGate.putRuntimeExtraValue("key", 1L); - } - - @Test - public void removeRuntimeExtrasValue() { - DeployGate.removeRuntimeExtraValue("key"); - } - - @Test - public void removeAllRuntimeExtraValues() { - DeployGate.removeAllRuntimeExtraValues(); - } - - @Test - public void putCustomValues() { - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid", "value")).isFalse(); - - DeployGate.install(app); - - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid", "value")).isTrue(); - } - - @Test - public void putCustomValues__keyPattern() { - DeployGate.install(app); - - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_underscore", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_1_number", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("min", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("ng", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("true", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("false", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("null", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid-hyphen", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid#sharp", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid$dollar", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid.dot", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid!bang", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid*glob", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalidUpperCase", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("12345", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("1_invalid_begin_number", "value")).isFalse(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_key_with_length_under_32", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid_key_with_length_over_32_characters", "value")).isFalse(); - - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_underscore", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_1_number", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("min", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("ng", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("true", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("false", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("null", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid-hyphen", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid#sharp", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid$dollar", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid.dot", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid!bang", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid*glob", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalidUpperCase", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("12345", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("1_invalid_begin_number", "value")).isFalse(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_key_with_length_under_32", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid_key_with_length_over_32_characters", "value")).isFalse(); - } - - @Test - public void putCustomValues__valuePattern() { - DeployGate.install(app); - - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_string", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_int", 1)).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_long", 1L)).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_float", 1.1f)).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_double", 1.1)).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("valid_boolean", true)).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("invalid_too_long_string", "this is too long string value. we cannot accept value if size over 64.")).isFalse(); - - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_string", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_int", 1)).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_long", 1L)).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_float", 1.1f)).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_double", 1.1)).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("valid_boolean", true)).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("invalid_too_long_string", "this is too long string value. we cannot accept value if size over 64.")).isFalse(); - } - - @Test - public void setBuildEnvironment__maxSize() { - DeployGate.install(app); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key2", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key3", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key4", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key5", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key6", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key7", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key8", "value")).isTrue(); - - // allow to overwrite - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value2")).isTrue(); - // not allow to put value with new key because of max size - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key9", "value")).isFalse(); - - DeployGate.removeBuildEnvironmentValue("key8"); - - // allow to put value with new key after remove exists key - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key9", "value")).isTrue(); - // allow to overwrite - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value3")).isTrue(); - // not allow to put value with new key because of max size - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key10", "value")).isFalse(); - - DeployGate.removeAllBuildEnvironmentValues(); - - // allow to put value less than max size - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key1", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key2", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key3", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key4", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key5", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key6", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key7", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key8", "value")).isTrue(); - Truth.assertThat(DeployGate.putBuildEnvironmentValue("key9", "value")).isFalse(); - } - - @Test - public void setRuntimeExtra__maxSize() { - DeployGate.install(app); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key2", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key3", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key4", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key5", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key6", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key7", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key8", "value")).isTrue(); - - // allow to overwrite - Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value2")).isTrue(); - // not allow to put value with new key because of max size - Truth.assertThat(DeployGate.putRuntimeExtraValue("key9", "value")).isFalse(); - - DeployGate.removeRuntimeExtraValue("key8"); - - // allow to put value with new key after remove exists key - Truth.assertThat(DeployGate.putRuntimeExtraValue("key9", "value")).isTrue(); - // allow to overwrite - Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value3")).isTrue(); - // not allow to put value with new key because of max size - Truth.assertThat(DeployGate.putRuntimeExtraValue("key10", "value")).isFalse(); - - DeployGate.removeAllRuntimeExtraValues(); - - // allow to put value less than max size - Truth.assertThat(DeployGate.putRuntimeExtraValue("key1", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key2", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key3", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key4", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key5", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key6", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key7", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key8", "value")).isTrue(); - Truth.assertThat(DeployGate.putRuntimeExtraValue("key9", "value")).isFalse(); + public void clearRuntimeExtra() { + DeployGate.clearRuntimeExtra(); } } From d8bcc7336b827e7b89d812dc22e2c56eb8a87e4f Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 18:08:04 +0900 Subject: [PATCH 08/34] fix(sdk): add CustomAttributes for capture feature --- .../com/deploygate/sdk/CustomAttributes.java | 116 +++++++++++ .../java/com/deploygate/sdk/DeployGate.java | 192 +++--------------- .../com/deploygate/sdk/CustomAttributes.java | 25 +++ .../java/com/deploygate/sdk/DeployGate.java | 56 +---- 4 files changed, 179 insertions(+), 210 deletions(-) create mode 100644 sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java create mode 100644 sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java new file mode 100644 index 0000000..f633ea5 --- /dev/null +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -0,0 +1,116 @@ +package com.deploygate.sdk; + +import android.util.Log; + +import org.json.JSONObject; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +public class CustomAttributes { + + private static final String TAG = "CustomAttributes"; + + private static final int MAX_ATTRIBUTES_SIZE = 8; + private static final Pattern VALID_KEY_PATTERN = Pattern.compile("^[a-z][_a-z0-9]{2,31}$"); + private static final int MIN_KEY_LENGTH = 3; + private static final int MAX_KEY_LENGTH = 32; + private static final int MAX_VALUE_LENGTH = 64; + + + private final ConcurrentHashMap attributes; + + public CustomAttributes() { + attributes = new ConcurrentHashMap<>(); + } + + public boolean putString(String key, String value) { + return putInternal(key, value); + } + + public boolean putInt(String key, int value) { + return putInternal(key, value); + } + + public boolean putLong(String key, long value) { + return putInternal(key, value); + } + + public boolean putFloat(String key, float value) { + return putInternal(key, value); + } + + public boolean putDouble(String key, double value) { + return putInternal(key, value); + } + + public boolean putBoolean(String key, boolean value) { + return putInternal(key, value); + } + + public void remove(String key) { + attributes.remove(key); + } + + public void removeAll() { + attributes.clear(); + } + + public int size() { + return attributes.size(); + } + + public String toJsonString() { + return new JSONObject(attributes).toString(); + } + + private boolean putInternal(String key, Object value) { + if (!isValidKey(key)) { + return false; + } + + if (!isValidValue(value)) { + return false; + } + + attributes.put(key, value); + return true; + } + + private boolean isValidKey(String key) { + if (size() >= MAX_ATTRIBUTES_SIZE && !attributes.containsKey(key)) { + Log.w(TAG, "Attributes already reached max size. Ignored: " + key); + return false; + } + + if (key == null || key.equals("true") || key.equals("false") || key.equals("null")) { + Log.w(TAG, "Not allowed key: " + key); + return false; + } + + if (key.length() < MIN_KEY_LENGTH || key.length() > MAX_KEY_LENGTH || !VALID_KEY_PATTERN.matcher(key).matches()) { + Log.w(TAG, "Invalid key: " + key); + return false; + } + + return true; + } + + private boolean isValidValue(Object value) { + if (value == null) { + Log.w(TAG, "Value is null"); + return false; + } + + if (value instanceof String && ((String) value).length() > MAX_VALUE_LENGTH) { + Log.w(TAG, "Value too long: " + value); + return false; + } else if (value instanceof String || value instanceof Number || value instanceof Boolean) { + return true; + } else { + // dead code + Log.w(TAG, "Invalid value: " + value); + return false; + } + } +} diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 1a2c239..e7089b4 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -28,7 +28,6 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; /** * This is DeployGate SDK library. Import this library to the application @@ -50,13 +49,6 @@ public class DeployGate { private static final String DEPLOYGATE_PACKAGE = "com.deploygate"; private static final Object sPendingEventLock = new Object(); - private static final int MAX_BUILD_ENVIRONMENTS_SIZE = 8; - private static final int MAX_RUNTIME_EXTRAS_SIZE = 8; - private static final Pattern USER_INPUT_KEY_PATTERN = Pattern.compile("^[a-z][_a-z0-9]{2,31}$"); - private static final int MIN_USER_INPUT_KEY_LENGTH = 3; - private static final int MAX_USER_INPUT_KEY_LENGTH = 32; - private static final int MAX_USER_INPUT_VALUE_LENGTH = 64; - private static DeployGate sInstance; private final Context mApplicationContext; @@ -67,8 +59,8 @@ public class DeployGate { private final CustomLogInstructionSerializer mCustomLogInstructionSerializer; private final HashSet mCallbacks; private final HashMap mPendingEvents; - private final HashMap mBuildEnvironments; - private final HashMap mRuntimeExtras; + private CustomAttributes mBuildEnvironment; + private CustomAttributes mRuntimeExtra; private final String mExpectedAuthor; private String mAuthor; @@ -289,8 +281,8 @@ private DeployGate( mCustomLogInstructionSerializer = new CustomLogInstructionSerializer(mHostApp.packageName, sdkConfiguration.customLogConfiguration); mCallbacks = new HashSet<>(); mPendingEvents = new HashMap<>(); - mBuildEnvironments = new HashMap<>(); - mRuntimeExtras = new HashMap<>(); + mBuildEnvironment = null; + mRuntimeExtra = null; mExpectedAuthor = sdkConfiguration.appOwnerName; prepareBroadcastReceiver(); @@ -1403,191 +1395,67 @@ public static String getDistributionUserName() { return sInstance.mDistributionUserName; } - public static boolean putBuildEnvironmentValue(String key, String value) { - if (sInstance != null) { - return sInstance.putBuildEnvironmentValueInternal(key, value); - } - return false; - } - - public static boolean putBuildEnvironmentValue(String key, int value) { - if (sInstance != null) { - return sInstance.putBuildEnvironmentValueInternal(key, value); - } - return false; - } - - public static boolean putBuildEnvironmentValue(String key, long value) { - if (sInstance != null) { - return sInstance.putBuildEnvironmentValueInternal(key, value); - } - return false; - } - - public static boolean putBuildEnvironmentValue(String key, float value) { - if (sInstance != null) { - return sInstance.putBuildEnvironmentValueInternal(key, value); - } - return false; - } - - public static boolean putBuildEnvironmentValue(String key, double value) { - if (sInstance != null) { - return sInstance.putBuildEnvironmentValueInternal(key, value); - } - return false; - } - - public static boolean putBuildEnvironmentValue(String key, boolean value) { - if (sInstance != null) { - return sInstance.putBuildEnvironmentValueInternal(key, value); - } - return false; - } - - public static void removeBuildEnvironmentValue(String key) { - if (sInstance != null) { - sInstance.removeBuildEnvironmentValueInternal(key); - } - } - - public static void removeAllBuildEnvironmentValues() { - if (sInstance != null) { - sInstance.removeAllBuildEnvironmentValuesInternal(); - } - } - - public static boolean putRuntimeExtraValue(String key, String value) { - if (sInstance != null) { - return sInstance.putRuntimeExtraValueInternal(key, value); - } - return false; - } - - public static boolean putRuntimeExtraValue(String key, int value) { + public static CustomAttributes getBuildEnvironment() { if (sInstance != null) { - return sInstance.putRuntimeExtraValueInternal(key, value); + return sInstance.getBuildEnvironmentInternal(); } - return false; + return null; } - public static boolean putRuntimeExtraValue(String key, long value) { + public static void setBuildEnvironment(CustomAttributes attrs) { if (sInstance != null) { - return sInstance.putRuntimeExtraValueInternal(key, value); + sInstance.setBuildEnvironmentInternal(attrs); } - return false; } - public static boolean putRuntimeExtraValue(String key, float value) { + public static void clearBuildEnvironment() { if (sInstance != null) { - return sInstance.putRuntimeExtraValueInternal(key, value); + sInstance.clearBuildEnvironmentInternal(); } - return false; } - public static boolean putRuntimeExtraValue(String key, double value) { + public static CustomAttributes getRuntimeExtra() { if (sInstance != null) { - return sInstance.putRuntimeExtraValueInternal(key, value); + return sInstance.getRuntimeExtraInternal(); } - return false; + return null; } - public static boolean putRuntimeExtraValue(String key, boolean value) { + public static void setRuntimeExtra(CustomAttributes attrs) { if (sInstance != null) { - return sInstance.putRuntimeExtraValueInternal(key, value); + sInstance.setRuntimeExtraInternal(attrs); } - return false; } - public static void removeRuntimeExtraValue(String key) { + public static void clearRuntimeExtra() { if (sInstance != null) { - sInstance.removeRuntimeExtraValueInternal(key); + sInstance.clearRuntimeExtraInternal(); } } - public static void removeAllRuntimeExtraValues() { - if (sInstance != null) { - sInstance.removeAllRuntimeExtraValuesInternal(); - } + private CustomAttributes getBuildEnvironmentInternal() { + return mBuildEnvironment; } - private boolean putBuildEnvironmentValueInternal(String key, Object value) { - if (mBuildEnvironments.size() >= MAX_BUILD_ENVIRONMENTS_SIZE && !mBuildEnvironments.containsKey(key)) { - Log.w(TAG, "BuildEnvironment already full. Ignored: " + key); - return false; - } - - if (!isValidKey(key)) { - return false; - } - - if (!isValidValue(value)) { - return false; - } - - mBuildEnvironments.put(key, value); - return true; + private void setBuildEnvironmentInternal(CustomAttributes attrs) { + mBuildEnvironment = attrs; } - private void removeBuildEnvironmentValueInternal(String key) { - mBuildEnvironments.remove(key); - } - private void removeAllBuildEnvironmentValuesInternal() { - mBuildEnvironments.clear(); + private void clearBuildEnvironmentInternal() { + mBuildEnvironment = null; } - private boolean putRuntimeExtraValueInternal(String key, Object value) { - if (mRuntimeExtras.size() >= MAX_RUNTIME_EXTRAS_SIZE && !mRuntimeExtras.containsKey(key)) { - Log.w(TAG, "RuntimeExtra already full. Ignored: " + key); - return false; - } - - if (!isValidKey(key)) { - return false; - } - - if (!isValidValue(value)) { - return false; - } - - mRuntimeExtras.put(key, value); - return true; + private CustomAttributes getRuntimeExtraInternal() { + return mRuntimeExtra; } - private void removeRuntimeExtraValueInternal(String key) { - mRuntimeExtras.remove(key); - } - private void removeAllRuntimeExtraValuesInternal() { - mRuntimeExtras.clear(); + private void setRuntimeExtraInternal(CustomAttributes attrs) { + mRuntimeExtra = attrs; } - private boolean isValidKey(String key) { - if (key == null || key.equals("true") || key.equals("false") || key.equals("null")) { - Log.w(TAG, "Not allowed key: " + key); - return false; - } - - if (key.length() < MIN_USER_INPUT_KEY_LENGTH || key.length() > MAX_USER_INPUT_KEY_LENGTH || !USER_INPUT_KEY_PATTERN.matcher(key).matches()) { - Log.w(TAG, "Invalid key: " + key); - return false; - } - - return true; - } - - private boolean isValidValue(Object value) { - if (value == null) { - Log.w(TAG, "Value is null"); - return false; - } - - if (value instanceof String && ((String) value).length() > MAX_USER_INPUT_VALUE_LENGTH) { - Log.w(TAG, "Value too long: " + value); - return false; - } - - return true; + private void clearRuntimeExtraInternal() { + mRuntimeExtra = null; } } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java new file mode 100644 index 0000000..c7305cb --- /dev/null +++ b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -0,0 +1,25 @@ +package com.deploygate.sdk; + +public class CustomAttributes { + + public CustomAttributes() {} + + public boolean put(String key, String value) { + return false; + } + + public boolean put(String key, Number value) { + return false; + } + + public boolean put(String key, Boolean value) { + return false; + } + + public void remove(String key) { + } + + public String toJsonString() { + return null; + } +} diff --git a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java index 963d408..ba9a136 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java @@ -180,63 +180,23 @@ public static String getDistributionUserName() { return null; } - public static boolean putBuildEnvironmentValue(String key, String value) { - return false; - } - - public static boolean putBuildEnvironmentValue(String key, int value) { - return false; - } - - public static boolean putBuildEnvironmentValue(String key, long value) { - return false; - } - - public static boolean putBuildEnvironmentValue(String key, float value) { - return false; - } - - public static boolean putBuildEnvironmentValue(String key, double value) { - return false; - } - - public static boolean putBuildEnvironmentValue(String key, boolean value) { - return false; - } - - public static void removeBuildEnvironmentValue(String key) { - } - - public static void removeAllBuildEnvironmentValues() { - } - - public static boolean putRuntimeExtraValue(String key, String value) { - return false; - } - - public static boolean putRuntimeExtraValue(String key, int value) { - return false; - } - - public static boolean putRuntimeExtraValue(String key, long value) { - return false; + public static CustomAttributes getBuildEnvironment() { + return null; } - public static boolean putRuntimeExtraValue(String key, float value) { - return false; + public static void setBuildEnvironment(CustomAttributes attrs) { } - public static boolean putRuntimeExtraValue(String key, double value) { - return false; + public static void clearBuildEnvironment() { } - public static boolean putRuntimeExtraValue(String key, boolean value) { - return false; + public static CustomAttributes getRuntimeExtra() { + return null; } - public static void removeRuntimeExtraValue(String key) { + public static void setRuntimeExtra(CustomAttributes attrs) { } - public static void removeAllRuntimeExtraValues() { + public static void clearRuntimeExtra() { } } From c5c8f3c21bddc7fe82f3e0cf84067d926f938dd7 Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 18:23:17 +0900 Subject: [PATCH 09/34] test(sdk): add CustomAttributesTest#toJsonString() --- .../deploygate/sdk/CustomAttributesTest.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java index 6350857..8223c4b 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -5,6 +5,7 @@ import com.google.common.truth.Truth; +import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -21,7 +22,7 @@ public void setUp() { } @Test - public void put__key_pattern() { + public void put__keyPattern() { Truth.assertThat(attributes.putString("valid", "value")).isTrue(); Truth.assertThat(attributes.putString("valid_underscore", "value")).isTrue(); Truth.assertThat(attributes.putString("valid_1_number", "value")).isTrue(); @@ -45,7 +46,7 @@ public void put__key_pattern() { } @Test - public void put__value_pattern() { + public void put__valuePattern() { Truth.assertThat(attributes.putString("valid_string", "value")).isTrue(); Truth.assertThat(attributes.putInt("valid_int", 1)).isTrue(); Truth.assertThat(attributes.putLong("valid_long", 1L)).isTrue(); @@ -57,7 +58,7 @@ public void put__value_pattern() { } @Test - public void put__max_size() { + public void put_remove_removeAll_maxSize() { Truth.assertThat(attributes.putString("key1", "value")).isTrue(); Truth.assertThat(attributes.putString("key2", "value")).isTrue(); Truth.assertThat(attributes.putString("key3", "value")).isTrue(); @@ -94,4 +95,26 @@ public void put__max_size() { Truth.assertThat(attributes.putString("key8", "value")).isTrue(); Truth.assertThat(attributes.putString("key9", "value")).isFalse(); } + + @Test + public void toJsonString() { + attributes.putString("valid_string", "value"); + attributes.putInt("valid_int", 1); + attributes.putLong("valid_long", 1L); + attributes.putFloat("valid_float", 1.1f); + attributes.putDouble("valid_double", 1.1); + attributes.putBoolean("valid_boolean", true); + + try { + JSONObject actualJson = new JSONObject(attributes.toJsonString()); + Truth.assertThat(actualJson.getString("valid_string")).isEqualTo("value"); + Truth.assertThat(actualJson.getInt("valid_int")).isEqualTo(1); + Truth.assertThat(actualJson.getLong("valid_long")).isEqualTo(1L); + Truth.assertThat((float) actualJson.getDouble("valid_float")).isEqualTo(1.1f); + Truth.assertThat(actualJson.getDouble("valid_double")).isEqualTo(1.1); + Truth.assertThat(actualJson.getBoolean("valid_boolean")).isTrue(); + } catch (Exception e) { + Truth.assertWithMessage("Failed to parse JSON").fail(); + } + } } From 9be4d9248864585c1cc3bd311faa5e8f7a8f77bf Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 18:31:55 +0900 Subject: [PATCH 10/34] feat(sdk): write custom attributes when collect device state event received --- sample/src/main/AndroidManifest.xml | 7 +++++++ .../main/java/com/deploygate/sdk/DeployGate.java | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 938dfec..f8cda70 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -15,6 +15,9 @@ + + + + + + + \ No newline at end of file diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index e7089b4..e8d2269 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -3,10 +3,13 @@ import android.app.Application; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -129,6 +132,18 @@ public void onEvent( } else { Logger.w("streamed logcat is not supported"); } + } else if (DeployGateEvent.ACTION_COLLECT_DEVICE_STATES.equals(action)) { + Uri targetUri = Uri.parse(extras.getString(DeployGateEvent.EXTRA_TARGET_URI_FOR_REPORT_DEVICE_STATES)); + Logger.d("collect-device-status event received: %s", targetUri); + + ContentValues cv = new ContentValues(); + cv.put(DeployGateEvent.KEY_BUILD_ENVIRONMENT, mBuildEnvironment.toJsonString()); + cv.put(DeployGateEvent.KEY_RUNTIME_EXTRAS, mRuntimeExtra.toJsonString()); + cv.put(DeployGateEvent.KEY_PACKAGE_NAME, mApplicationContext.getPackageName()); + cv.put(DeployGateEvent.KEY_EVENT_AT, System.currentTimeMillis()); + + ContentResolver cr = mApplicationContext.getContentResolver(); + cr.insert(targetUri, cv); } else { Logger.w("%s is not supported by this sdk version", action); } From 480de522cc5e8feeea312922d5dff05f8e56f86c Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 18:33:23 +0900 Subject: [PATCH 11/34] chore(sample): update sample code set custom attributes --- .../main/java/com/deploygate/sample/App.java | 24 +++++++++++++++++++ .../com/deploygate/sample/SampleActivity.java | 14 +++++++++++ 2 files changed, 38 insertions(+) diff --git a/sample/src/main/java/com/deploygate/sample/App.java b/sample/src/main/java/com/deploygate/sample/App.java index cce2f7f..e4c29a3 100644 --- a/sample/src/main/java/com/deploygate/sample/App.java +++ b/sample/src/main/java/com/deploygate/sample/App.java @@ -1,9 +1,11 @@ package com.deploygate.sample; import android.app.Application; +import android.content.res.Configuration; import android.os.Bundle; import android.util.Log; +import com.deploygate.sdk.CustomAttributes; import com.deploygate.sdk.DeployGate; import com.deploygate.sdk.DeployGateCallback; @@ -34,6 +36,14 @@ public void onInitialized(boolean isServiceAvailable) { if (isServiceAvailable) { Log.i(TAG, "SDK is available"); DeployGate.logInfo("SDK is available"); + + CustomAttributes attrs = new CustomAttributes(); + attrs.putString("build_type", BuildConfig.BUILD_TYPE); + attrs.putString("flavor", BuildConfig.FLAVOR); + attrs.putString("version_name", BuildConfig.VERSION_NAME); + attrs.putInt("version_code", BuildConfig.VERSION_CODE); + attrs.putString("application_id", BuildConfig.APPLICATION_ID); + DeployGate.setRuntimeExtra(attrs); } else { Log.i(TAG, "SDK is unavailable"); DeployGate.logInfo("SDK is unavailable"); // this fails silently @@ -84,4 +94,18 @@ public void onUpdateAvailable( // // You can use DeployGate.isAuthorized() later to check the installation is valid or not. } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + CustomAttributes attrs = DeployGate.getRuntimeExtra(); + if (attrs == null) { + attrs = new CustomAttributes(); + } + attrs.putString("locale", newConfig.locale.toString()); + attrs.putInt("orientation", newConfig.orientation); + attrs.putFloat("font_scale", newConfig.fontScale); + DeployGate.setRuntimeExtra(attrs); + } } diff --git a/sample/src/main/java/com/deploygate/sample/SampleActivity.java b/sample/src/main/java/com/deploygate/sample/SampleActivity.java index 5e69045..18e6eff 100644 --- a/sample/src/main/java/com/deploygate/sample/SampleActivity.java +++ b/sample/src/main/java/com/deploygate/sample/SampleActivity.java @@ -14,6 +14,7 @@ import android.widget.TextView; import android.widget.Toast; +import com.deploygate.sdk.CustomAttributes; import com.deploygate.sdk.DeployGate; import com.deploygate.sdk.DeployGateCallback; @@ -53,6 +54,19 @@ public void onCreate(Bundle savedInstanceState) { mUpdateButton = (Button) findViewById(R.id.updateButton); mLogMessage = (EditText) findViewById(R.id.message); mDistributionComments = (LinearLayout) findViewById(R.id.distributionComments); + + + CustomAttributes attrs = DeployGate.getRuntimeExtra(); + if (attrs == null) { + attrs = new CustomAttributes(); + } + attrs.putString("string", "value"); + attrs.putInt("int", 123); + attrs.putBoolean("boolean", true); + attrs.putFloat("float", 1.23f); + attrs.putDouble("double", 1.23); + attrs.putLong("long", 123L); + DeployGate.setRuntimeExtra(attrs); } @Override From 6b56334f32b03c323cda4731a5667bb56105ba2b Mon Sep 17 00:00:00 2001 From: satsukies Date: Mon, 22 Jul 2024 18:44:26 +0900 Subject: [PATCH 12/34] fix(sdk): fix build failed due to missing some methods in sdkMock --- .../com/deploygate/sdk/CustomAttributes.java | 1 - .../com/deploygate/sdk/CustomAttributes.java | 28 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index f633ea5..a897e3a 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -17,7 +17,6 @@ public class CustomAttributes { private static final int MAX_KEY_LENGTH = 32; private static final int MAX_VALUE_LENGTH = 64; - private final ConcurrentHashMap attributes; public CustomAttributes() { diff --git a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java index c7305cb..c994c65 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -2,23 +2,43 @@ public class CustomAttributes { - public CustomAttributes() {} + public CustomAttributes() { + } + + public boolean putString(String key, String value) { + return false; + } + + public boolean putInt(String key, int value) { + return false; + } - public boolean put(String key, String value) { + public boolean putLong(String key, long value) { return false; } - public boolean put(String key, Number value) { + public boolean putFloat(String key, float value) { return false; } - public boolean put(String key, Boolean value) { + public boolean putDouble(String key, double value) { + return false; + } + + public boolean putBoolean(String key, boolean value) { return false; } public void remove(String key) { } + public void removeAll() { + } + + public int size() { + return 0; + } + public String toJsonString() { return null; } From 11d51ab434287e754f5b9260d7364390c2cb3917 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 00:56:55 +0900 Subject: [PATCH 13/34] fix(sdk): don't send values when it is null --- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index e8d2269..85f0495 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -137,8 +137,12 @@ public void onEvent( Logger.d("collect-device-status event received: %s", targetUri); ContentValues cv = new ContentValues(); - cv.put(DeployGateEvent.KEY_BUILD_ENVIRONMENT, mBuildEnvironment.toJsonString()); - cv.put(DeployGateEvent.KEY_RUNTIME_EXTRAS, mRuntimeExtra.toJsonString()); + if (mBuildEnvironment != null) { + cv.put(DeployGateEvent.KEY_BUILD_ENVIRONMENT, mBuildEnvironment.toJsonString()); + } + if (mRuntimeExtra != null) { + cv.put(DeployGateEvent.KEY_RUNTIME_EXTRAS, mRuntimeExtra.toJsonString()); + } cv.put(DeployGateEvent.KEY_PACKAGE_NAME, mApplicationContext.getPackageName()); cv.put(DeployGateEvent.KEY_EVENT_AT, System.currentTimeMillis()); From 2b8eceeb97ce7224dfd9dff3b0d1ce3e88056b65 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 00:58:24 +0900 Subject: [PATCH 14/34] fix(sdk): don't send package name from SDK --- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 85f0495..1013ea4 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -143,7 +143,6 @@ public void onEvent( if (mRuntimeExtra != null) { cv.put(DeployGateEvent.KEY_RUNTIME_EXTRAS, mRuntimeExtra.toJsonString()); } - cv.put(DeployGateEvent.KEY_PACKAGE_NAME, mApplicationContext.getPackageName()); cv.put(DeployGateEvent.KEY_EVENT_AT, System.currentTimeMillis()); ContentResolver cr = mApplicationContext.getContentResolver(); From 95dad512ffc966964cb84e9741fec6b3e193b012 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 01:35:31 +0900 Subject: [PATCH 15/34] fix(sdk): provide getter only --- .../com/deploygate/sdk/CustomAttributes.java | 4 + .../java/com/deploygate/sdk/DeployGate.java | 82 ++++++------------- .../sdk/DeployGateInterfaceTest.java | 24 +----- .../java/com/deploygate/sdk/DeployGate.java | 12 --- 4 files changed, 30 insertions(+), 92 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index a897e3a..8078517 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -59,6 +59,10 @@ public int size() { return attributes.size(); } + public boolean isEmpty() { + return attributes.isEmpty(); + } + public String toJsonString() { return new JSONObject(attributes).toString(); } diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 1013ea4..106a14d 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -52,6 +52,10 @@ public class DeployGate { private static final String DEPLOYGATE_PACKAGE = "com.deploygate"; private static final Object sPendingEventLock = new Object(); + private static final Object sLock = new Object(); + private static CustomAttributes sBuildEnvironment = null; + private static CustomAttributes sRuntimeExtra = null; + private static DeployGate sInstance; private final Context mApplicationContext; @@ -62,8 +66,6 @@ public class DeployGate { private final CustomLogInstructionSerializer mCustomLogInstructionSerializer; private final HashSet mCallbacks; private final HashMap mPendingEvents; - private CustomAttributes mBuildEnvironment; - private CustomAttributes mRuntimeExtra; private final String mExpectedAuthor; private String mAuthor; @@ -137,11 +139,11 @@ public void onEvent( Logger.d("collect-device-status event received: %s", targetUri); ContentValues cv = new ContentValues(); - if (mBuildEnvironment != null) { - cv.put(DeployGateEvent.KEY_BUILD_ENVIRONMENT, mBuildEnvironment.toJsonString()); + if (sBuildEnvironment != null && !sBuildEnvironment.isEmpty()) { + cv.put(DeployGateEvent.KEY_BUILD_ENVIRONMENT, sBuildEnvironment.toJsonString()); } - if (mRuntimeExtra != null) { - cv.put(DeployGateEvent.KEY_RUNTIME_EXTRAS, mRuntimeExtra.toJsonString()); + if (sRuntimeExtra != null && !sRuntimeExtra.isEmpty()) { + cv.put(DeployGateEvent.KEY_RUNTIME_EXTRAS, sRuntimeExtra.toJsonString()); } cv.put(DeployGateEvent.KEY_EVENT_AT, System.currentTimeMillis()); @@ -299,8 +301,6 @@ private DeployGate( mCustomLogInstructionSerializer = new CustomLogInstructionSerializer(mHostApp.packageName, sdkConfiguration.customLogConfiguration); mCallbacks = new HashSet<>(); mPendingEvents = new HashMap<>(); - mBuildEnvironment = null; - mRuntimeExtra = null; mExpectedAuthor = sdkConfiguration.appOwnerName; prepareBroadcastReceiver(); @@ -1414,66 +1414,32 @@ public static String getDistributionUserName() { } public static CustomAttributes getBuildEnvironment() { - if (sInstance != null) { - return sInstance.getBuildEnvironmentInternal(); + if (sBuildEnvironment != null) { + return sBuildEnvironment; } - return null; - } - public static void setBuildEnvironment(CustomAttributes attrs) { - if (sInstance != null) { - sInstance.setBuildEnvironmentInternal(attrs); + synchronized (sLock) { + if (sBuildEnvironment != null) { + return sBuildEnvironment; + } + sBuildEnvironment = new CustomAttributes(); } - } - public static void clearBuildEnvironment() { - if (sInstance != null) { - sInstance.clearBuildEnvironmentInternal(); - } + return sBuildEnvironment; } public static CustomAttributes getRuntimeExtra() { - if (sInstance != null) { - return sInstance.getRuntimeExtraInternal(); - } - return null; - } - - public static void setRuntimeExtra(CustomAttributes attrs) { - if (sInstance != null) { - sInstance.setRuntimeExtraInternal(attrs); + if (sRuntimeExtra != null) { + return sRuntimeExtra; } - } - public static void clearRuntimeExtra() { - if (sInstance != null) { - sInstance.clearRuntimeExtraInternal(); + synchronized (sLock) { + if (sRuntimeExtra != null) { + return sRuntimeExtra; + } + sRuntimeExtra = new CustomAttributes(); } - } - - private CustomAttributes getBuildEnvironmentInternal() { - return mBuildEnvironment; - } - - private void setBuildEnvironmentInternal(CustomAttributes attrs) { - mBuildEnvironment = attrs; - } - - - private void clearBuildEnvironmentInternal() { - mBuildEnvironment = null; - } - - private CustomAttributes getRuntimeExtraInternal() { - return mRuntimeExtra; - } - - - private void setRuntimeExtraInternal(CustomAttributes attrs) { - mRuntimeExtra = attrs; - } - private void clearRuntimeExtraInternal() { - mRuntimeExtra = null; + return sRuntimeExtra; } } diff --git a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java index d7570d1..1a142ec 100644 --- a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java @@ -367,31 +367,11 @@ public void getDistributionUserName() { @Test public void getBuildEnvironment() { - Truth.assertThat(DeployGate.getBuildEnvironment()).isNull(); - } - - @Test - public void setBuildEnvironment() { - DeployGate.setBuildEnvironment(null); - } - - @Test - public void clearBuildEnvironment() { - DeployGate.clearBuildEnvironment(); + DeployGate.getBuildEnvironment(); } @Test public void getRuntimeExtra() { - Truth.assertThat(DeployGate.getRuntimeExtra()).isNull(); - } - - @Test - public void setRuntimeExtra() { - DeployGate.setRuntimeExtra(null); - } - - @Test - public void clearRuntimeExtra() { - DeployGate.clearRuntimeExtra(); + DeployGate.getRuntimeExtra(); } } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java index ba9a136..a103b11 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java @@ -184,19 +184,7 @@ public static CustomAttributes getBuildEnvironment() { return null; } - public static void setBuildEnvironment(CustomAttributes attrs) { - } - - public static void clearBuildEnvironment() { - } - public static CustomAttributes getRuntimeExtra() { return null; } - - public static void setRuntimeExtra(CustomAttributes attrs) { - } - - public static void clearRuntimeExtra() { - } } From 4889e37fd380da51de7813faf98ffa94fa16db7f Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 01:39:57 +0900 Subject: [PATCH 16/34] fix(sdk): fix too general naming of some constant values --- .../main/java/com/deploygate/sdk/DeployGate.java | 6 +++--- .../com/deploygate/service/DeployGateEvent.java | 13 ++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 106a14d..03dc861 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -140,12 +140,12 @@ public void onEvent( ContentValues cv = new ContentValues(); if (sBuildEnvironment != null && !sBuildEnvironment.isEmpty()) { - cv.put(DeployGateEvent.KEY_BUILD_ENVIRONMENT, sBuildEnvironment.toJsonString()); + cv.put(DeployGateEvent.ATTRIBUTE_KEY_BUILD_ENVIRONMENT, sBuildEnvironment.toJsonString()); } if (sRuntimeExtra != null && !sRuntimeExtra.isEmpty()) { - cv.put(DeployGateEvent.KEY_RUNTIME_EXTRAS, sRuntimeExtra.toJsonString()); + cv.put(DeployGateEvent.ATTRIBUTE_KEY_RUNTIME_EXTRAS, sRuntimeExtra.toJsonString()); } - cv.put(DeployGateEvent.KEY_EVENT_AT, System.currentTimeMillis()); + cv.put(DeployGateEvent.ATTRIBUTE_KEY_EVENT_AT, System.currentTimeMillis()); ContentResolver cr = mApplicationContext.getContentResolver(); cr.insert(targetUri, cv); diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index 540f40b..1e4f36a 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -8,7 +8,7 @@ public interface DeployGateEvent { // namespace: // ACTION => a // EXTRA => e - // KEY => k + // ATTRIBUTE_KEY => ak // // content: // should be hyphen-separated string and be lower cases @@ -135,22 +135,17 @@ public interface DeployGateEvent { /** * @since 4.8.0 */ - public static final String KEY_BUILD_ENVIRONMENT = "k.build-environment"; + public static final String ATTRIBUTE_KEY_BUILD_ENVIRONMENT = "ak.build-environment"; /** * @since 4.8.0 */ - public static final String KEY_RUNTIME_EXTRAS = "k.runtime-extras"; + public static final String ATTRIBUTE_KEY_RUNTIME_EXTRAS = "ak.runtime-extras"; /** * @since 4.8.0 */ - public static final String KEY_PACKAGE_NAME = "k.package-name"; - - /** - * @since 4.8.0 - */ - public static final String KEY_EVENT_AT = "k.event-at"; + public static final String ATTRIBUTE_KEY_EVENT_AT = "ak.event-at"; interface VisibilityType { int BACKGROUND = 0; From 823463e6ae9452848f440c0250ac9343babbc874 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 01:51:57 +0900 Subject: [PATCH 17/34] fix(sdk): class became final and hide constructor from outside --- .../main/java/com/deploygate/sdk/CustomAttributes.java | 6 +++--- .../main/java/com/deploygate/sdk/CustomAttributes.java | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index 8078517..f552682 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -7,7 +7,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; -public class CustomAttributes { +public final class CustomAttributes { private static final String TAG = "CustomAttributes"; @@ -19,7 +19,7 @@ public class CustomAttributes { private final ConcurrentHashMap attributes; - public CustomAttributes() { + CustomAttributes() { attributes = new ConcurrentHashMap<>(); } @@ -63,7 +63,7 @@ public boolean isEmpty() { return attributes.isEmpty(); } - public String toJsonString() { + String toJsonString() { return new JSONObject(attributes).toString(); } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java index c994c65..0fd256e 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -1,8 +1,8 @@ package com.deploygate.sdk; -public class CustomAttributes { +public final class CustomAttributes { - public CustomAttributes() { + CustomAttributes() { } public boolean putString(String key, String value) { @@ -39,7 +39,11 @@ public int size() { return 0; } - public String toJsonString() { + public boolean isEmpty() { + return true; + } + + String toJsonString() { return null; } } From 71e158f53b54ae474b2ce3c363b95e69b104975f Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 02:11:05 +0900 Subject: [PATCH 18/34] fix(sdk): simplify redundant condition --- sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index f552682..8985c74 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -13,8 +13,6 @@ public final class CustomAttributes { private static final int MAX_ATTRIBUTES_SIZE = 8; private static final Pattern VALID_KEY_PATTERN = Pattern.compile("^[a-z][_a-z0-9]{2,31}$"); - private static final int MIN_KEY_LENGTH = 3; - private static final int MAX_KEY_LENGTH = 32; private static final int MAX_VALUE_LENGTH = 64; private final ConcurrentHashMap attributes; @@ -91,7 +89,7 @@ private boolean isValidKey(String key) { return false; } - if (key.length() < MIN_KEY_LENGTH || key.length() > MAX_KEY_LENGTH || !VALID_KEY_PATTERN.matcher(key).matches()) { + if (!VALID_KEY_PATTERN.matcher(key).matches()) { Log.w(TAG, "Invalid key: " + key); return false; } From 9bcb4f1b26542b46e6c7a8331333cbc52d80a956 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 02:12:19 +0900 Subject: [PATCH 19/34] fix(sdk): improve put operation for thread safety --- .../com/deploygate/sdk/CustomAttributes.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index 8985c74..0dc22a2 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -66,16 +66,18 @@ String toJsonString() { } private boolean putInternal(String key, Object value) { - if (!isValidKey(key)) { - return false; - } + synchronized (attributes) { + if (!isValidKey(key)) { + return false; + } - if (!isValidValue(value)) { - return false; - } + if (!isValidValue(value)) { + return false; + } - attributes.put(key, value); - return true; + attributes.put(key, value); + return true; + } } private boolean isValidKey(String key) { From 9c16ebf7d95dbd69b91b6a33f80ade782b1d8681 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 02:16:04 +0900 Subject: [PATCH 20/34] fix(sdk): use Logger instead of Log --- .../java/com/deploygate/sdk/CustomAttributes.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index 0dc22a2..efc0681 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -1,6 +1,6 @@ package com.deploygate.sdk; -import android.util.Log; +import com.deploygate.sdk.internal.Logger; import org.json.JSONObject; @@ -82,17 +82,17 @@ private boolean putInternal(String key, Object value) { private boolean isValidKey(String key) { if (size() >= MAX_ATTRIBUTES_SIZE && !attributes.containsKey(key)) { - Log.w(TAG, "Attributes already reached max size. Ignored: " + key); + Logger.w(TAG, "Attributes already reached max size. Ignored: " + key); return false; } if (key == null || key.equals("true") || key.equals("false") || key.equals("null")) { - Log.w(TAG, "Not allowed key: " + key); + Logger.w(TAG, "Not allowed key: " + key); return false; } if (!VALID_KEY_PATTERN.matcher(key).matches()) { - Log.w(TAG, "Invalid key: " + key); + Logger.w(TAG, "Invalid key: " + key); return false; } @@ -101,18 +101,18 @@ private boolean isValidKey(String key) { private boolean isValidValue(Object value) { if (value == null) { - Log.w(TAG, "Value is null"); + Logger.w(TAG, "Value is null"); return false; } if (value instanceof String && ((String) value).length() > MAX_VALUE_LENGTH) { - Log.w(TAG, "Value too long: " + value); + Logger.w(TAG, "Value too long: " + value); return false; } else if (value instanceof String || value instanceof Number || value instanceof Boolean) { return true; } else { // dead code - Log.w(TAG, "Invalid value: " + value); + Logger.w(TAG, "Invalid value: " + value); return false; } } From f1a9aff127623d88af40f04dc9eeae5deb1d80a2 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 02:31:29 +0900 Subject: [PATCH 21/34] test(sdk): add CustomAttributesInterfaceTest --- .../sdk/CustomAttributesInterfaceTest.java | 68 +++++++++++++++++++ .../sdk/CustomAttributesInterfaceTest.java | 1 + 2 files changed, 69 insertions(+) create mode 100644 sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java create mode 120000 sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java new file mode 100644 index 0000000..3b5337f --- /dev/null +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java @@ -0,0 +1,68 @@ +package com.deploygate.sdk; + +import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This test class will make sure all *public* interfaces are defined as expected + */ +@RunWith(AndroidJUnit4.class) +public class CustomAttributesInterfaceTest { + + @NonNull + CustomAttributes attributes; + + @Before + public void setUp() { + attributes = new CustomAttributes(); + } + + @Test + public void putString() { + attributes.putString("key", "value"); + } + + @Test + public void putInt() { + attributes.putInt("key", 1); + } + + @Test + public void putLong() { + attributes.putLong("key", 1L); + } + + @Test + public void putFloat() { + attributes.putFloat("key", 1.0f); + } + + @Test + public void putDouble() { + attributes.putDouble("key", 1.0); + } + + public void putBoolean() { + attributes.putBoolean("key", true); + } + + public void remove() { + attributes.remove("key"); + } + + public void removeAll() { + attributes.removeAll(); + } + + public int size() { + return attributes.size(); + } + + public boolean isEmpty() { + return attributes.isEmpty(); + } +} \ No newline at end of file diff --git a/sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java b/sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java new file mode 120000 index 0000000..c93ad84 --- /dev/null +++ b/sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java @@ -0,0 +1 @@ +../../../../../../../sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java \ No newline at end of file From 854288a802202c8b0606c73dce3ae1636689caec Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 02:32:24 +0900 Subject: [PATCH 22/34] chore(sample): improvement sample codes --- sample/src/main/java/com/deploygate/sample/App.java | 10 +--------- .../java/com/deploygate/sample/SampleActivity.java | 4 ---- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/sample/src/main/java/com/deploygate/sample/App.java b/sample/src/main/java/com/deploygate/sample/App.java index e4c29a3..6bc710c 100644 --- a/sample/src/main/java/com/deploygate/sample/App.java +++ b/sample/src/main/java/com/deploygate/sample/App.java @@ -37,13 +37,9 @@ public void onInitialized(boolean isServiceAvailable) { Log.i(TAG, "SDK is available"); DeployGate.logInfo("SDK is available"); - CustomAttributes attrs = new CustomAttributes(); + CustomAttributes attrs = DeployGate.getBuildEnvironment(); attrs.putString("build_type", BuildConfig.BUILD_TYPE); attrs.putString("flavor", BuildConfig.FLAVOR); - attrs.putString("version_name", BuildConfig.VERSION_NAME); - attrs.putInt("version_code", BuildConfig.VERSION_CODE); - attrs.putString("application_id", BuildConfig.APPLICATION_ID); - DeployGate.setRuntimeExtra(attrs); } else { Log.i(TAG, "SDK is unavailable"); DeployGate.logInfo("SDK is unavailable"); // this fails silently @@ -100,12 +96,8 @@ public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); CustomAttributes attrs = DeployGate.getRuntimeExtra(); - if (attrs == null) { - attrs = new CustomAttributes(); - } attrs.putString("locale", newConfig.locale.toString()); attrs.putInt("orientation", newConfig.orientation); attrs.putFloat("font_scale", newConfig.fontScale); - DeployGate.setRuntimeExtra(attrs); } } diff --git a/sample/src/main/java/com/deploygate/sample/SampleActivity.java b/sample/src/main/java/com/deploygate/sample/SampleActivity.java index 18e6eff..c8894ca 100644 --- a/sample/src/main/java/com/deploygate/sample/SampleActivity.java +++ b/sample/src/main/java/com/deploygate/sample/SampleActivity.java @@ -57,16 +57,12 @@ public void onCreate(Bundle savedInstanceState) { CustomAttributes attrs = DeployGate.getRuntimeExtra(); - if (attrs == null) { - attrs = new CustomAttributes(); - } attrs.putString("string", "value"); attrs.putInt("int", 123); attrs.putBoolean("boolean", true); attrs.putFloat("float", 1.23f); attrs.putDouble("double", 1.23); attrs.putLong("long", 123L); - DeployGate.setRuntimeExtra(attrs); } @Override From c8296588cddc3eca4ee56ce779c631e4764aa8e6 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 10:21:14 +0900 Subject: [PATCH 23/34] test(sdk): fix symbolic link --- .../java/com/deploygate/sdk/CustomAttributesInterfaceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java b/sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java index c93ad84..cc4e30b 120000 --- a/sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java +++ b/sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java @@ -1 +1 @@ -../../../../../../../sdkMock/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java \ No newline at end of file +../../../../../../../sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java \ No newline at end of file From 1c5407dac261b7f7a0242220a13a5f85e90c2cc0 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 15:40:23 +0900 Subject: [PATCH 24/34] test(sdk): fix testcase method naming --- .../test/java/com/deploygate/sdk/CustomAttributesTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java index 8223c4b..4ed7252 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -22,7 +22,7 @@ public void setUp() { } @Test - public void put__keyPattern() { + public void put__accept_when_valid_key() { Truth.assertThat(attributes.putString("valid", "value")).isTrue(); Truth.assertThat(attributes.putString("valid_underscore", "value")).isTrue(); Truth.assertThat(attributes.putString("valid_1_number", "value")).isTrue(); @@ -46,7 +46,7 @@ public void put__keyPattern() { } @Test - public void put__valuePattern() { + public void put__accept_when_valid_value() { Truth.assertThat(attributes.putString("valid_string", "value")).isTrue(); Truth.assertThat(attributes.putInt("valid_int", 1)).isTrue(); Truth.assertThat(attributes.putLong("valid_long", 1L)).isTrue(); @@ -58,7 +58,7 @@ public void put__valuePattern() { } @Test - public void put_remove_removeAll_maxSize() { + public void not_exceed_max_size() { Truth.assertThat(attributes.putString("key1", "value")).isTrue(); Truth.assertThat(attributes.putString("key2", "value")).isTrue(); Truth.assertThat(attributes.putString("key3", "value")).isTrue(); From 45ac031a5b29e6800b10dff3a2598a8107facdcd Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 20:14:18 +0900 Subject: [PATCH 25/34] fix(sdk): all public method always return non-null value --- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 4 ++-- .../src/main/java/com/deploygate/sdk/CustomAttributes.java | 2 +- sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 03dc861..db4faa8 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -53,8 +53,8 @@ public class DeployGate { private static final Object sPendingEventLock = new Object(); private static final Object sLock = new Object(); - private static CustomAttributes sBuildEnvironment = null; - private static CustomAttributes sRuntimeExtra = null; + private static CustomAttributes sBuildEnvironment; + private static CustomAttributes sRuntimeExtra; private static DeployGate sInstance; diff --git a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java index 0fd256e..705afd3 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -44,6 +44,6 @@ public boolean isEmpty() { } String toJsonString() { - return null; + return "{}"; } } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java index a103b11..5d0b765 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/DeployGate.java @@ -5,6 +5,8 @@ public class DeployGate { + private static final CustomAttributes sAttributes = new CustomAttributes(); + static void clear() { } @@ -181,10 +183,10 @@ public static String getDistributionUserName() { } public static CustomAttributes getBuildEnvironment() { - return null; + return sAttributes; } public static CustomAttributes getRuntimeExtra() { - return null; + return sAttributes; } } From 45084e10114b71cebed6048a9698ac010126b1d9 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 20:15:31 +0900 Subject: [PATCH 26/34] test(sdk): add testcase checking behavior of isEmpty() and size() --- .../deploygate/sdk/CustomAttributesTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java index 4ed7252..ffbb4e0 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -96,6 +96,53 @@ public void not_exceed_max_size() { Truth.assertThat(attributes.putString("key9", "value")).isFalse(); } + @Test + public void size() { + Truth.assertThat(attributes.size()).isEqualTo(0); + + attributes.putString("key1", "value"); + Truth.assertThat(attributes.size()).isEqualTo(1); + + attributes.putString("key2", "value"); + attributes.putString("key3", "value"); + attributes.putString("key4", "value"); + attributes.putString("key5", "value"); + attributes.putString("key6", "value"); + attributes.putString("key7", "value"); + attributes.putString("key8", "value"); + Truth.assertThat(attributes.size()).isEqualTo(8); + + attributes.putString("key9", "value"); + Truth.assertThat(attributes.size()).isEqualTo(8); + + attributes.remove("key1"); + Truth.assertThat(attributes.size()).isEqualTo(7); + + attributes.removeAll(); + Truth.assertThat(attributes.size()).isEqualTo(0); + } + + @Test + public void isEmpty() { + Truth.assertThat(attributes.isEmpty()).isTrue(); + + attributes.putString("key1", "value"); + Truth.assertThat(attributes.isEmpty()).isFalse(); + + attributes.putString("key2", "value"); + attributes.putString("key3", "value"); + Truth.assertThat(attributes.isEmpty()).isFalse(); + + attributes.remove("key1"); + Truth.assertThat(attributes.isEmpty()).isFalse(); + + attributes.removeAll(); + Truth.assertThat(attributes.isEmpty()).isTrue(); + + attributes.putString("key4", "value"); + Truth.assertThat(attributes.isEmpty()).isFalse(); + } + @Test public void toJsonString() { attributes.putString("valid_string", "value"); From 4519aa28bc6ac00d2f25373cc119ab9f96d6cb6b Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 20:17:05 +0900 Subject: [PATCH 27/34] fix(sdk): use try-catch insert value via content resolver --- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index db4faa8..96c7930 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -147,8 +147,12 @@ public void onEvent( } cv.put(DeployGateEvent.ATTRIBUTE_KEY_EVENT_AT, System.currentTimeMillis()); - ContentResolver cr = mApplicationContext.getContentResolver(); - cr.insert(targetUri, cv); + try { + ContentResolver cr = mApplicationContext.getContentResolver(); + cr.insert(targetUri, cv); + } catch (Throwable t) { + Logger.w(t, "failed to report device states"); + } } else { Logger.w("%s is not supported by this sdk version", action); } From 024b6cc9b87c296c3bc8a6de231ecb8daf4a3198 Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 20:34:10 +0900 Subject: [PATCH 28/34] fix(sdk): append prefix to keys when call toJsonString() --- .../com/deploygate/sdk/CustomAttributes.java | 18 +++++++++++++++++- .../java/com/deploygate/sdk/DeployGate.java | 13 +++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index efc0681..3ad7f5b 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -4,6 +4,7 @@ import org.json.JSONObject; +import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; @@ -62,7 +63,22 @@ public boolean isEmpty() { } String toJsonString() { - return new JSONObject(attributes).toString(); + return toJsonString(""); + } + + String toJsonString(String keyPrefix) { + synchronized (attributes) { + if (keyPrefix != null && !keyPrefix.isEmpty()) { + HashMap prefixedAttributes = new HashMap<>(); + for (String key : attributes.keySet()) { + String prefixedKey = String.format("%s.%s", keyPrefix, key); + prefixedAttributes.put(prefixedKey, attributes.get(key)); + } + return new JSONObject(prefixedAttributes).toString(); + } else { + return new JSONObject(attributes).toString(); + } + } } private boolean putInternal(String key, Object value) { diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 96c7930..92d7b75 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -139,12 +139,17 @@ public void onEvent( Logger.d("collect-device-status event received: %s", targetUri); ContentValues cv = new ContentValues(); - if (sBuildEnvironment != null && !sBuildEnvironment.isEmpty()) { - cv.put(DeployGateEvent.ATTRIBUTE_KEY_BUILD_ENVIRONMENT, sBuildEnvironment.toJsonString()); + String buildEnvironmentJson = getBuildEnvironment().toJsonString(); + if (!buildEnvironmentJson.equals("{}")) { + cv.put(DeployGateEvent.ATTRIBUTE_KEY_BUILD_ENVIRONMENT, buildEnvironmentJson); } - if (sRuntimeExtra != null && !sRuntimeExtra.isEmpty()) { - cv.put(DeployGateEvent.ATTRIBUTE_KEY_RUNTIME_EXTRAS, sRuntimeExtra.toJsonString()); + + // attribute keys of runtime extra need to prefix with the package name + String runtimeExtraJson = getRuntimeExtra().toJsonString(mHostApp.packageName); + if (!runtimeExtraJson.equals("{}")) { + cv.put(DeployGateEvent.ATTRIBUTE_KEY_RUNTIME_EXTRAS, runtimeExtraJson); } + cv.put(DeployGateEvent.ATTRIBUTE_KEY_EVENT_AT, System.currentTimeMillis()); try { From 1627bffe0ace5db8c040b4a5c03025f96e15090f Mon Sep 17 00:00:00 2001 From: satsukies Date: Tue, 23 Jul 2024 21:34:17 +0900 Subject: [PATCH 29/34] fix(sdk): use synchronized when access CustomAttributes --- .../com/deploygate/sdk/CustomAttributes.java | 24 +-------- .../java/com/deploygate/sdk/DeployGate.java | 49 ++++++++++++++++--- .../deploygate/sdk/CustomAttributesTest.java | 23 --------- .../com/deploygate/sdk/CustomAttributes.java | 4 -- 4 files changed, 44 insertions(+), 56 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index 3ad7f5b..233475f 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -2,9 +2,6 @@ import com.deploygate.sdk.internal.Logger; -import org.json.JSONObject; - -import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; @@ -16,7 +13,7 @@ public final class CustomAttributes { private static final Pattern VALID_KEY_PATTERN = Pattern.compile("^[a-z][_a-z0-9]{2,31}$"); private static final int MAX_VALUE_LENGTH = 64; - private final ConcurrentHashMap attributes; + final ConcurrentHashMap attributes; CustomAttributes() { attributes = new ConcurrentHashMap<>(); @@ -62,25 +59,6 @@ public boolean isEmpty() { return attributes.isEmpty(); } - String toJsonString() { - return toJsonString(""); - } - - String toJsonString(String keyPrefix) { - synchronized (attributes) { - if (keyPrefix != null && !keyPrefix.isEmpty()) { - HashMap prefixedAttributes = new HashMap<>(); - for (String key : attributes.keySet()) { - String prefixedKey = String.format("%s.%s", keyPrefix, key); - prefixedAttributes.put(prefixedKey, attributes.get(key)); - } - return new JSONObject(prefixedAttributes).toString(); - } else { - return new JSONObject(attributes).toString(); - } - } - } - private boolean putInternal(String key, Object value) { synchronized (attributes) { if (!isValidKey(key)) { diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 92d7b75..8b307e8 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -24,11 +24,14 @@ import com.deploygate.service.IDeployGateSdkService; import com.deploygate.service.IDeployGateSdkServiceCallback; +import org.json.JSONObject; + import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -55,6 +58,7 @@ public class DeployGate { private static final Object sLock = new Object(); private static CustomAttributes sBuildEnvironment; private static CustomAttributes sRuntimeExtra; + private static CustomAttributes sSdkRuntimeExtra; private static DeployGate sInstance; @@ -139,14 +143,32 @@ public void onEvent( Logger.d("collect-device-status event received: %s", targetUri); ContentValues cv = new ContentValues(); - String buildEnvironmentJson = getBuildEnvironment().toJsonString(); - if (!buildEnvironmentJson.equals("{}")) { - cv.put(DeployGateEvent.ATTRIBUTE_KEY_BUILD_ENVIRONMENT, buildEnvironmentJson); + synchronized (getBuildEnvironment().attributes) { + if (!getBuildEnvironment().attributes.isEmpty()) { + String buildEnvironmentJson = new JSONObject(getBuildEnvironment().attributes).toString(); + cv.put(DeployGateEvent.ATTRIBUTE_KEY_BUILD_ENVIRONMENT, buildEnvironmentJson); + } + } + + ConcurrentHashMap mergedRuntimeExtraHashMap = new ConcurrentHashMap<>(); + // attribute keys of runtime extra created by user need to prefix with the package name + synchronized (getRuntimeExtra().attributes) { + if (!getRuntimeExtra().attributes.isEmpty()) { + for (Map.Entry attr : getRuntimeExtra().attributes.entrySet()) { + String userKey = String.format("%s.%s", mHostApp.packageName, attr.getKey()); + mergedRuntimeExtraHashMap.put(userKey, attr.getValue()); + } + } + } + + synchronized (getSdkRuntimeExtra().attributes) { + if (!getSdkRuntimeExtra().attributes.isEmpty()) { + mergedRuntimeExtraHashMap.putAll(getSdkRuntimeExtra().attributes); + } } - // attribute keys of runtime extra need to prefix with the package name - String runtimeExtraJson = getRuntimeExtra().toJsonString(mHostApp.packageName); - if (!runtimeExtraJson.equals("{}")) { + if (!mergedRuntimeExtraHashMap.isEmpty()) { + String runtimeExtraJson = new JSONObject(mergedRuntimeExtraHashMap).toString(); cv.put(DeployGateEvent.ATTRIBUTE_KEY_RUNTIME_EXTRAS, runtimeExtraJson); } @@ -1451,4 +1473,19 @@ public static CustomAttributes getRuntimeExtra() { return sRuntimeExtra; } + + private static CustomAttributes getSdkRuntimeExtra() { + if (sSdkRuntimeExtra != null) { + return sSdkRuntimeExtra; + } + + synchronized (sLock) { + if (sSdkRuntimeExtra != null) { + return sSdkRuntimeExtra; + } + sSdkRuntimeExtra = new CustomAttributes(); + } + + return sSdkRuntimeExtra; + } } diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java index ffbb4e0..eb23c3b 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -5,7 +5,6 @@ import com.google.common.truth.Truth; -import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -142,26 +141,4 @@ public void isEmpty() { attributes.putString("key4", "value"); Truth.assertThat(attributes.isEmpty()).isFalse(); } - - @Test - public void toJsonString() { - attributes.putString("valid_string", "value"); - attributes.putInt("valid_int", 1); - attributes.putLong("valid_long", 1L); - attributes.putFloat("valid_float", 1.1f); - attributes.putDouble("valid_double", 1.1); - attributes.putBoolean("valid_boolean", true); - - try { - JSONObject actualJson = new JSONObject(attributes.toJsonString()); - Truth.assertThat(actualJson.getString("valid_string")).isEqualTo("value"); - Truth.assertThat(actualJson.getInt("valid_int")).isEqualTo(1); - Truth.assertThat(actualJson.getLong("valid_long")).isEqualTo(1L); - Truth.assertThat((float) actualJson.getDouble("valid_float")).isEqualTo(1.1f); - Truth.assertThat(actualJson.getDouble("valid_double")).isEqualTo(1.1); - Truth.assertThat(actualJson.getBoolean("valid_boolean")).isTrue(); - } catch (Exception e) { - Truth.assertWithMessage("Failed to parse JSON").fail(); - } - } } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java index 705afd3..83b78f0 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -42,8 +42,4 @@ public int size() { public boolean isEmpty() { return true; } - - String toJsonString() { - return "{}"; - } } From 6f0ebd84870df42516c6ecf41acfbf29c3a49592 Mon Sep 17 00:00:00 2001 From: satsukies Date: Wed, 24 Jul 2024 19:00:32 +0900 Subject: [PATCH 30/34] test(sdk): use different values, check return type --- .../sdk/CustomAttributesInterfaceTest.java | 27 ++-- .../deploygate/sdk/CustomAttributesTest.java | 116 +++++++++--------- .../sdk/DeployGateInterfaceTest.java | 4 +- 3 files changed, 80 insertions(+), 67 deletions(-) diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java index 3b5337f..8f4350c 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java @@ -3,6 +3,8 @@ import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.truth.Truth; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -23,46 +25,51 @@ public void setUp() { @Test public void putString() { - attributes.putString("key", "value"); + Truth.assertThat(attributes.putString("key", "value")).isInstanceOf(Boolean.class); } @Test public void putInt() { - attributes.putInt("key", 1); + Truth.assertThat(attributes.putInt("key", 1)).isInstanceOf(Boolean.class); } @Test public void putLong() { - attributes.putLong("key", 1L); + Truth.assertThat(attributes.putLong("key", 1L)).isInstanceOf(Boolean.class); } @Test public void putFloat() { - attributes.putFloat("key", 1.0f); + Truth.assertThat(attributes.putFloat("key", 1.0f)).isInstanceOf(Boolean.class); } @Test public void putDouble() { - attributes.putDouble("key", 1.0); + Truth.assertThat(attributes.putDouble("key", 1.0)).isInstanceOf(Boolean.class); } + @Test public void putBoolean() { - attributes.putBoolean("key", true); + Truth.assertThat(attributes.putBoolean("key", true)).isInstanceOf(Boolean.class); } + @Test public void remove() { attributes.remove("key"); } + @Test public void removeAll() { attributes.removeAll(); } - public int size() { - return attributes.size(); + @Test + public void size() { + Truth.assertThat(attributes.size()).isInstanceOf(Integer.class); } - public boolean isEmpty() { - return attributes.isEmpty(); + @Test + public void isEmpty() { + Truth.assertThat(attributes.isEmpty()).isInstanceOf(Boolean.class); } } \ No newline at end of file diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java index eb23c3b..bdf7fc1 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -22,26 +22,26 @@ public void setUp() { @Test public void put__accept_when_valid_key() { - Truth.assertThat(attributes.putString("valid", "value")).isTrue(); - Truth.assertThat(attributes.putString("valid_underscore", "value")).isTrue(); - Truth.assertThat(attributes.putString("valid_1_number", "value")).isTrue(); - Truth.assertThat(attributes.putString("min", "value")).isTrue(); - Truth.assertThat(attributes.putString("valid_key_with_length_under_32", "value")).isTrue(); - - Truth.assertThat(attributes.putString("ng", "value")).isFalse(); - Truth.assertThat(attributes.putString("true", "value")).isFalse(); - Truth.assertThat(attributes.putString("false", "value")).isFalse(); - Truth.assertThat(attributes.putString("null", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalid-hyphen", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalid#sharp", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalid$dollar", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalid.dot", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalid!bang", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalid*glob", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalidUpperCase", "value")).isFalse(); - Truth.assertThat(attributes.putString("12345", "value")).isFalse(); - Truth.assertThat(attributes.putString("1_invalid_begin_number", "value")).isFalse(); - Truth.assertThat(attributes.putString("invalid_key_with_length_over_32_characters", "value")).isFalse(); + Truth.assertThat(attributes.putString("valid", "valid_value1")).isTrue(); + Truth.assertThat(attributes.putString("valid_underscore", "valid_value2")).isTrue(); + Truth.assertThat(attributes.putString("valid_1_number", "valid_value3")).isTrue(); + Truth.assertThat(attributes.putString("min", "valid_value4")).isTrue(); + Truth.assertThat(attributes.putString("valid_key_with_length_under_32", "valid_value5")).isTrue(); + + Truth.assertThat(attributes.putString("ng", "invalid_value1")).isFalse(); + Truth.assertThat(attributes.putString("true", "invalid_value2")).isFalse(); + Truth.assertThat(attributes.putString("false", "invalid_value3")).isFalse(); + Truth.assertThat(attributes.putString("null", "invalid_value4")).isFalse(); + Truth.assertThat(attributes.putString("invalid-hyphen", "invalid_value5")).isFalse(); + Truth.assertThat(attributes.putString("invalid#sharp", "invalid_value6")).isFalse(); + Truth.assertThat(attributes.putString("invalid$dollar", "invalid_value7")).isFalse(); + Truth.assertThat(attributes.putString("invalid.dot", "invalid_value8")).isFalse(); + Truth.assertThat(attributes.putString("invalid!bang", "invalid_value9")).isFalse(); + Truth.assertThat(attributes.putString("invalid*glob", "invalid_value10")).isFalse(); + Truth.assertThat(attributes.putString("invalidUpperCase", "invalid_value11")).isFalse(); + Truth.assertThat(attributes.putString("12345", "invalid_value12")).isFalse(); + Truth.assertThat(attributes.putString("1_invalid_begin_number", "invalid_value13")).isFalse(); + Truth.assertThat(attributes.putString("invalid_key_with_length_over_32_characters", "invalid_value14")).isFalse(); } @Test @@ -58,60 +58,63 @@ public void put__accept_when_valid_value() { @Test public void not_exceed_max_size() { - Truth.assertThat(attributes.putString("key1", "value")).isTrue(); - Truth.assertThat(attributes.putString("key2", "value")).isTrue(); - Truth.assertThat(attributes.putString("key3", "value")).isTrue(); - Truth.assertThat(attributes.putString("key4", "value")).isTrue(); - Truth.assertThat(attributes.putString("key5", "value")).isTrue(); - Truth.assertThat(attributes.putString("key6", "value")).isTrue(); - Truth.assertThat(attributes.putString("key7", "value")).isTrue(); - Truth.assertThat(attributes.putString("key8", "value")).isTrue(); + Truth.assertThat(attributes.putString("key1", "value1")).isTrue(); + Truth.assertThat(attributes.putString("key2", "value2")).isTrue(); + Truth.assertThat(attributes.putString("key3", "value3")).isTrue(); + Truth.assertThat(attributes.putString("key4", "value4")).isTrue(); + Truth.assertThat(attributes.putString("key5", "value5")).isTrue(); + Truth.assertThat(attributes.putString("key6", "value6")).isTrue(); + Truth.assertThat(attributes.putString("key7", "value7")).isTrue(); + Truth.assertThat(attributes.putString("key8", "value8")).isTrue(); // allow to overwrite - Truth.assertThat(attributes.putString("key1", "value2")).isTrue(); + Truth.assertThat(attributes.putString("key1", "overwrite1_1")).isTrue(); // not allow to put value with new key because of max size - Truth.assertThat(attributes.putString("key9", "value")).isFalse(); + Truth.assertThat(attributes.putString("key9", "value9")).isFalse(); attributes.remove("key8"); // allow to put value with new key after remove exists key - Truth.assertThat(attributes.putString("key9", "value")).isTrue(); + Truth.assertThat(attributes.putString("key9", "value9")).isTrue(); // allow to overwrite - Truth.assertThat(attributes.putString("key1", "value3")).isTrue(); + Truth.assertThat(attributes.putString("key1", "overwrite1_2")).isTrue(); // not allow to put value with new key because of max size - Truth.assertThat(attributes.putString("key10", "value")).isFalse(); + Truth.assertThat(attributes.putString("key10", "value10")).isFalse(); attributes.removeAll(); // allow to put value less than max size - Truth.assertThat(attributes.putString("key1", "value")).isTrue(); - Truth.assertThat(attributes.putString("key2", "value")).isTrue(); - Truth.assertThat(attributes.putString("key3", "value")).isTrue(); - Truth.assertThat(attributes.putString("key4", "value")).isTrue(); - Truth.assertThat(attributes.putString("key5", "value")).isTrue(); - Truth.assertThat(attributes.putString("key6", "value")).isTrue(); - Truth.assertThat(attributes.putString("key7", "value")).isTrue(); - Truth.assertThat(attributes.putString("key8", "value")).isTrue(); - Truth.assertThat(attributes.putString("key9", "value")).isFalse(); + Truth.assertThat(attributes.putString("key1", "another_value1")).isTrue(); + Truth.assertThat(attributes.putString("key2", "another_value2")).isTrue(); + Truth.assertThat(attributes.putString("key3", "another_value3")).isTrue(); + Truth.assertThat(attributes.putString("key4", "another_value4")).isTrue(); + Truth.assertThat(attributes.putString("key5", "another_value5")).isTrue(); + Truth.assertThat(attributes.putString("key6", "another_value6")).isTrue(); + Truth.assertThat(attributes.putString("key7", "another_value7")).isTrue(); + Truth.assertThat(attributes.putString("key8", "another_value8")).isTrue(); + Truth.assertThat(attributes.putString("key9", "another_value9")).isFalse(); } @Test public void size() { Truth.assertThat(attributes.size()).isEqualTo(0); - attributes.putString("key1", "value"); + attributes.putString("key1", "value1"); Truth.assertThat(attributes.size()).isEqualTo(1); - attributes.putString("key2", "value"); - attributes.putString("key3", "value"); - attributes.putString("key4", "value"); - attributes.putString("key5", "value"); - attributes.putString("key6", "value"); - attributes.putString("key7", "value"); - attributes.putString("key8", "value"); + attributes.putString("key1", "overwrite1"); + Truth.assertThat(attributes.size()).isEqualTo(1); + + attributes.putString("key2", "value2"); + attributes.putString("key3", "value3"); + attributes.putString("key4", "value4"); + attributes.putString("key5", "value5"); + attributes.putString("key6", "value6"); + attributes.putString("key7", "value7"); + attributes.putString("key8", "value8"); Truth.assertThat(attributes.size()).isEqualTo(8); - attributes.putString("key9", "value"); + attributes.putString("key9", "value9"); Truth.assertThat(attributes.size()).isEqualTo(8); attributes.remove("key1"); @@ -125,11 +128,14 @@ public void size() { public void isEmpty() { Truth.assertThat(attributes.isEmpty()).isTrue(); - attributes.putString("key1", "value"); + attributes.putString("key1", "value1"); + Truth.assertThat(attributes.isEmpty()).isFalse(); + + attributes.putString("key1", "overwrite1"); Truth.assertThat(attributes.isEmpty()).isFalse(); - attributes.putString("key2", "value"); - attributes.putString("key3", "value"); + attributes.putString("key2", "value2"); + attributes.putString("key3", "value3"); Truth.assertThat(attributes.isEmpty()).isFalse(); attributes.remove("key1"); @@ -138,7 +144,7 @@ public void isEmpty() { attributes.removeAll(); Truth.assertThat(attributes.isEmpty()).isTrue(); - attributes.putString("key4", "value"); + attributes.putString("key4", "value4"); Truth.assertThat(attributes.isEmpty()).isFalse(); } } diff --git a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java index 1a142ec..62684ee 100644 --- a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java @@ -367,11 +367,11 @@ public void getDistributionUserName() { @Test public void getBuildEnvironment() { - DeployGate.getBuildEnvironment(); + Truth.assertThat(DeployGate.getBuildEnvironment()).isNotNull(); } @Test public void getRuntimeExtra() { - DeployGate.getRuntimeExtra(); + Truth.assertThat(DeployGate.getRuntimeExtra()).isNotNull(); } } From 7a510d6bc16af15e078a1574be199e76df0f0b35 Mon Sep 17 00:00:00 2001 From: satsukies Date: Thu, 25 Jul 2024 01:10:50 +0900 Subject: [PATCH 31/34] fix(sdk): fix CustomAttributes class become thread-safe --- .../com/deploygate/sdk/CustomAttributes.java | 69 +++++++---- .../java/com/deploygate/sdk/DeployGate.java | 26 +--- .../sdk/CustomAttributesInterfaceTest.java | 10 -- .../deploygate/sdk/CustomAttributesTest.java | 111 ++++++++++-------- .../com/deploygate/sdk/CustomAttributes.java | 8 -- 5 files changed, 113 insertions(+), 111 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index 233475f..64b8483 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -2,7 +2,9 @@ import com.deploygate.sdk.internal.Logger; -import java.util.concurrent.ConcurrentHashMap; +import org.json.JSONException; +import org.json.JSONObject; + import java.util.regex.Pattern; public final class CustomAttributes { @@ -13,10 +15,12 @@ public final class CustomAttributes { private static final Pattern VALID_KEY_PATTERN = Pattern.compile("^[a-z][_a-z0-9]{2,31}$"); private static final int MAX_VALUE_LENGTH = 64; - final ConcurrentHashMap attributes; + private final Object mLock; + private JSONObject attributes; CustomAttributes() { - attributes = new ConcurrentHashMap<>(); + mLock = new Object(); + attributes = new JSONObject(); } public boolean putString(String key, String value) { @@ -44,42 +48,59 @@ public boolean putBoolean(String key, boolean value) { } public void remove(String key) { - attributes.remove(key); + synchronized (mLock) { + attributes.remove(key); + } } public void removeAll() { - attributes.clear(); + synchronized (mLock) { + // recreate new object instead of removing all keys + attributes = new JSONObject(); + } } - public int size() { - return attributes.size(); + int size() { + synchronized (mLock) { + return attributes.length(); + } } - public boolean isEmpty() { - return attributes.isEmpty(); + String getJSONString() { + synchronized (mLock) { + return attributes.toString(); + } } private boolean putInternal(String key, Object value) { - synchronized (attributes) { - if (!isValidKey(key)) { - return false; - } + if (!isValidKey(key)) { + return false; + } - if (!isValidValue(value)) { + if (!isValidValue(value)) { + return false; + } + + synchronized (mLock) { + try { + attributes.put(key, value); + + if (attributes.length() > MAX_ATTRIBUTES_SIZE) { + // rollback put operation + attributes.remove(key); + Logger.w(TAG, "Attributes already reached max size. Ignored: " + key); + return false; + } + } catch (JSONException e) { + Logger.w(TAG, "Failed to put attribute: " + key, e); return false; } - - attributes.put(key, value); - return true; } + + return true; } private boolean isValidKey(String key) { - if (size() >= MAX_ATTRIBUTES_SIZE && !attributes.containsKey(key)) { - Logger.w(TAG, "Attributes already reached max size. Ignored: " + key); - return false; - } - if (key == null || key.equals("true") || key.equals("false") || key.equals("null")) { Logger.w(TAG, "Not allowed key: " + key); return false; @@ -100,8 +121,8 @@ private boolean isValidValue(Object value) { } if (value instanceof String && ((String) value).length() > MAX_VALUE_LENGTH) { - Logger.w(TAG, "Value too long: " + value); - return false; + Logger.w(TAG, "Value too long: " + value); + return false; } else if (value instanceof String || value instanceof Number || value instanceof Boolean) { return true; } else { diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 8b307e8..d34e3ff 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -31,7 +31,6 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -143,28 +142,15 @@ public void onEvent( Logger.d("collect-device-status event received: %s", targetUri); ContentValues cv = new ContentValues(); - synchronized (getBuildEnvironment().attributes) { - if (!getBuildEnvironment().attributes.isEmpty()) { - String buildEnvironmentJson = new JSONObject(getBuildEnvironment().attributes).toString(); - cv.put(DeployGateEvent.ATTRIBUTE_KEY_BUILD_ENVIRONMENT, buildEnvironmentJson); - } - } - ConcurrentHashMap mergedRuntimeExtraHashMap = new ConcurrentHashMap<>(); - // attribute keys of runtime extra created by user need to prefix with the package name - synchronized (getRuntimeExtra().attributes) { - if (!getRuntimeExtra().attributes.isEmpty()) { - for (Map.Entry attr : getRuntimeExtra().attributes.entrySet()) { - String userKey = String.format("%s.%s", mHostApp.packageName, attr.getKey()); - mergedRuntimeExtraHashMap.put(userKey, attr.getValue()); - } - } + String buildEnvironmentJSON = getBuildEnvironment().getJSONString(); + if (!buildEnvironmentJSON.equals("{}")) { + cv.put(DeployGateEvent.ATTRIBUTE_KEY_BUILD_ENVIRONMENT, buildEnvironmentJSON); } - synchronized (getSdkRuntimeExtra().attributes) { - if (!getSdkRuntimeExtra().attributes.isEmpty()) { - mergedRuntimeExtraHashMap.putAll(getSdkRuntimeExtra().attributes); - } + String runtimeExtraJSON = getRuntimeExtra().getJSONString(); + if (!runtimeExtraJSON.equals("{}")) { + cv.put(DeployGateEvent.ATTRIBUTE_KEY_RUNTIME_EXTRAS, runtimeExtraJSON); } if (!mergedRuntimeExtraHashMap.isEmpty()) { diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java index 8f4350c..b75071b 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesInterfaceTest.java @@ -62,14 +62,4 @@ public void remove() { public void removeAll() { attributes.removeAll(); } - - @Test - public void size() { - Truth.assertThat(attributes.size()).isInstanceOf(Integer.class); - } - - @Test - public void isEmpty() { - Truth.assertThat(attributes.isEmpty()).isInstanceOf(Boolean.class); - } } \ No newline at end of file diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java index bdf7fc1..c19b564 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -9,6 +9,9 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + @RunWith(AndroidJUnit4.class) public class CustomAttributesTest { @@ -53,9 +56,39 @@ public void put__accept_when_valid_value() { Truth.assertThat(attributes.putDouble("valid_double", 1.1)).isTrue(); Truth.assertThat(attributes.putBoolean("valid_boolean", true)).isTrue(); + String tooLongString = "this is too long string value. we cannot accept value if size over 64."; Truth.assertThat(attributes.putString("invalid_too_long_string", "this is too long string value. we cannot accept value if size over 64.")).isFalse(); } + @Test + public void size() { + Truth.assertThat(attributes.size()).isEqualTo(0); + + attributes.putString("key1", "value1"); + Truth.assertThat(attributes.size()).isEqualTo(1); + + attributes.putString("key1", "overwrite1"); + Truth.assertThat(attributes.size()).isEqualTo(1); + + attributes.putString("key2", "value2"); + attributes.putString("key3", "value3"); + attributes.putString("key4", "value4"); + attributes.putString("key5", "value5"); + attributes.putString("key6", "value6"); + attributes.putString("key7", "value7"); + attributes.putString("key8", "value8"); + Truth.assertThat(attributes.size()).isEqualTo(8); + + attributes.putString("key9", "value9"); + Truth.assertThat(attributes.size()).isEqualTo(8); + + attributes.remove("key1"); + Truth.assertThat(attributes.size()).isEqualTo(7); + + attributes.removeAll(); + Truth.assertThat(attributes.size()).isEqualTo(0); + } + @Test public void not_exceed_max_size() { Truth.assertThat(attributes.putString("key1", "value1")).isTrue(); @@ -95,56 +128,36 @@ public void not_exceed_max_size() { Truth.assertThat(attributes.putString("key9", "another_value9")).isFalse(); } - @Test - public void size() { - Truth.assertThat(attributes.size()).isEqualTo(0); - - attributes.putString("key1", "value1"); - Truth.assertThat(attributes.size()).isEqualTo(1); + @Test() + public void not_exceed_max_size_multi_thread() { + // prepare attributes with max size + for (int i = 0; i < 8; i++) { + attributes.putString("key" + i, "value" + i); + } + + // try to put value with multi thread + ExecutorService executors = Executors.newCachedThreadPool(); + for (int i = 0; i < 100; i++) { + final int index = i; + executors.submit(new Runnable() { + @Override + public void run() { + attributes.putString("key" + index, "value" + index); + } + }); + } - attributes.putString("key1", "overwrite1"); - Truth.assertThat(attributes.size()).isEqualTo(1); - - attributes.putString("key2", "value2"); - attributes.putString("key3", "value3"); - attributes.putString("key4", "value4"); - attributes.putString("key5", "value5"); - attributes.putString("key6", "value6"); - attributes.putString("key7", "value7"); - attributes.putString("key8", "value8"); Truth.assertThat(attributes.size()).isEqualTo(8); - - attributes.putString("key9", "value9"); - Truth.assertThat(attributes.size()).isEqualTo(8); - - attributes.remove("key1"); - Truth.assertThat(attributes.size()).isEqualTo(7); - - attributes.removeAll(); - Truth.assertThat(attributes.size()).isEqualTo(0); - } - - @Test - public void isEmpty() { - Truth.assertThat(attributes.isEmpty()).isTrue(); - - attributes.putString("key1", "value1"); - Truth.assertThat(attributes.isEmpty()).isFalse(); - - attributes.putString("key1", "overwrite1"); - Truth.assertThat(attributes.isEmpty()).isFalse(); - - attributes.putString("key2", "value2"); - attributes.putString("key3", "value3"); - Truth.assertThat(attributes.isEmpty()).isFalse(); - - attributes.remove("key1"); - Truth.assertThat(attributes.isEmpty()).isFalse(); - - attributes.removeAll(); - Truth.assertThat(attributes.isEmpty()).isTrue(); - - attributes.putString("key4", "value4"); - Truth.assertThat(attributes.isEmpty()).isFalse(); + String expectedJSON = "{" + + "\"key0\":\"value0\"," + + "\"key1\":\"value1\"," + + "\"key2\":\"value2\"," + + "\"key3\":\"value3\"," + + "\"key4\":\"value4\"," + + "\"key5\":\"value5\"," + + "\"key6\":\"value6\"," + + "\"key7\":\"value7\"" + + "}"; + Truth.assertThat(attributes.getJSONString()).isEqualTo(expectedJSON); } } diff --git a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java index 83b78f0..cdfc4f2 100644 --- a/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdkMock/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -34,12 +34,4 @@ public void remove(String key) { public void removeAll() { } - - public int size() { - return 0; - } - - public boolean isEmpty() { - return true; - } } From 7391f7f4a52e7dd57fa9039b81d01f34d988a2a2 Mon Sep 17 00:00:00 2001 From: satsukies Date: Thu, 25 Jul 2024 01:13:30 +0900 Subject: [PATCH 32/34] fix(sdk): send attributes collect by SDK --- .../java/com/deploygate/sdk/DeployGate.java | 22 +++++++++---------- .../deploygate/service/DeployGateEvent.java | 5 +++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index d34e3ff..cdb35bf 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -57,7 +57,7 @@ public class DeployGate { private static final Object sLock = new Object(); private static CustomAttributes sBuildEnvironment; private static CustomAttributes sRuntimeExtra; - private static CustomAttributes sSdkRuntimeExtra; + private static CustomAttributes sSdkDeviceStates; private static DeployGate sInstance; @@ -153,9 +153,9 @@ public void onEvent( cv.put(DeployGateEvent.ATTRIBUTE_KEY_RUNTIME_EXTRAS, runtimeExtraJSON); } - if (!mergedRuntimeExtraHashMap.isEmpty()) { - String runtimeExtraJson = new JSONObject(mergedRuntimeExtraHashMap).toString(); - cv.put(DeployGateEvent.ATTRIBUTE_KEY_RUNTIME_EXTRAS, runtimeExtraJson); + String sdkDeviceStatusJSON = getSdkDeviceStates().getJSONString(); + if (!sdkDeviceStatusJSON.equals("{}")) { + cv.put(DeployGateEvent.ATTRIBUTE_KEY_SDK_DEVICE_STATES, sdkDeviceStatusJSON); } cv.put(DeployGateEvent.ATTRIBUTE_KEY_EVENT_AT, System.currentTimeMillis()); @@ -1460,18 +1460,18 @@ public static CustomAttributes getRuntimeExtra() { return sRuntimeExtra; } - private static CustomAttributes getSdkRuntimeExtra() { - if (sSdkRuntimeExtra != null) { - return sSdkRuntimeExtra; + private static CustomAttributes getSdkDeviceStates() { + if (sSdkDeviceStates != null) { + return sSdkDeviceStates; } synchronized (sLock) { - if (sSdkRuntimeExtra != null) { - return sSdkRuntimeExtra; + if (sSdkDeviceStates != null) { + return sSdkDeviceStates; } - sSdkRuntimeExtra = new CustomAttributes(); + sSdkDeviceStates = new CustomAttributes(); } - return sSdkRuntimeExtra; + return sSdkDeviceStates; } } diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index 1e4f36a..ad77118 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -142,6 +142,11 @@ public interface DeployGateEvent { */ public static final String ATTRIBUTE_KEY_RUNTIME_EXTRAS = "ak.runtime-extras"; + /** + * @since 4.8.0 + */ + public static final String ATTRIBUTE_KEY_SDK_DEVICE_STATES = "ak.sdk-device-states"; + /** * @since 4.8.0 */ From 110a00e70f64e542e690db047aa08cf3f0062c9b Mon Sep 17 00:00:00 2001 From: satsukies Date: Thu, 25 Jul 2024 01:20:10 +0900 Subject: [PATCH 33/34] chore(sdk): documentation --- .../com/deploygate/sdk/CustomAttributes.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java index 64b8483..d35e7b9 100644 --- a/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java +++ b/sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java @@ -7,6 +7,10 @@ import java.util.regex.Pattern; +/** + * This class provides store key-value pairs. + * These methods are thread-safe. + */ public final class CustomAttributes { private static final String TAG = "CustomAttributes"; @@ -23,36 +27,91 @@ public final class CustomAttributes { attributes = new JSONObject(); } + /** + * Put a string value with the key. + * If the key already exists, the value will be overwritten. + * @param key key must be non-null and match the valid pattern. + * @param value value must be non-null and its length must be less than 64. + * @return true if the value is put successfully, otherwise false. + * @see CustomAttributes#VALID_KEY_PATTERN + */ public boolean putString(String key, String value) { return putInternal(key, value); } + /** + * Put an int value with the key. + * If the key already exists, the value will be overwritten. + * @param key key must be non-null and match the valid pattern. + * @param value int value + * @return true if the value is put successfully, otherwise false. + * @see CustomAttributes#VALID_KEY_PATTERN + */ public boolean putInt(String key, int value) { return putInternal(key, value); } + /** + * Put a long value with the key. + * If the key already exists, the value will be overwritten. + * @param key key must be non-null and match the valid pattern. + * @param value long value + * @return true if the value is put successfully, otherwise false. + * @see CustomAttributes#VALID_KEY_PATTERN + */ public boolean putLong(String key, long value) { return putInternal(key, value); } + /** + * Put a float value with the key. + * If the key already exists, the value will be overwritten. + * @param key key must be non-null and match the valid pattern. + * @param value float value + * @return true if the value is put successfully, otherwise false. + * @see CustomAttributes#VALID_KEY_PATTERN + */ public boolean putFloat(String key, float value) { return putInternal(key, value); } + /** + * Put a double value with the key. + * If the key already exists, the value will be overwritten. + * @param key key must be non-null and match the valid pattern. + * @param value double value + * @return true if the value is put successfully, otherwise false. + * @see CustomAttributes#VALID_KEY_PATTERN + */ public boolean putDouble(String key, double value) { return putInternal(key, value); } + /** + * Put a boolean value with the key. + * If the key already exists, the value will be overwritten. + * @param key key must be non-null and match the valid pattern. + * @param value boolean value + * @return true if the value is put successfully, otherwise false. + * @see CustomAttributes#VALID_KEY_PATTERN + */ public boolean putBoolean(String key, boolean value) { return putInternal(key, value); } + /** + * Remove the value with the key. + * @param key name of the key to be removed. + */ public void remove(String key) { synchronized (mLock) { attributes.remove(key); } } + /** + * Remove all key-value pairs. + */ public void removeAll() { synchronized (mLock) { // recreate new object instead of removing all keys From 063fc76fc75f85c696de214028f0477c2915c37b Mon Sep 17 00:00:00 2001 From: satsukies Date: Thu, 25 Jul 2024 12:55:41 +0900 Subject: [PATCH 34/34] test(sdk): add testcase of toJSONString() --- .../deploygate/sdk/CustomAttributesTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java index c19b564..9ac62eb 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomAttributesTest.java @@ -89,6 +89,46 @@ public void size() { Truth.assertThat(attributes.size()).isEqualTo(0); } + @Test + public void toJSONString() { + Truth.assertThat(attributes.getJSONString()).isEqualTo("{}"); + + attributes.putString("string", "value"); + attributes.putInt("int", Integer.MAX_VALUE); + attributes.putLong("long", Long.MAX_VALUE); + attributes.putFloat("float", Float.MAX_VALUE); + attributes.putDouble("double", Double.MAX_VALUE); + attributes.putBoolean("boolean", true); + + String expectedJSON = "{" + + "\"string\":\"value\"," + + "\"int\":2147483647," + + "\"long\":9223372036854775807," + + "\"float\":3.4028235E38," + + "\"double\":1.7976931348623157E308," + + "\"boolean\":true" + + "}"; + Truth.assertThat(attributes.getJSONString()).isEqualTo(expectedJSON); + + attributes.removeAll(); + attributes.putString("string2", "value2"); + attributes.putInt("int2", Integer.MIN_VALUE); + attributes.putLong("long2", Long.MIN_VALUE); + attributes.putFloat("float2", Float.MIN_VALUE); + attributes.putDouble("double2", Double.MIN_VALUE); + attributes.putBoolean("boolean2", false); + + String expectedJSON2 = "{" + + "\"string2\":\"value2\"," + + "\"int2\":-2147483648," + + "\"long2\":-9223372036854775808," + + "\"float2\":1.4E-45," + + "\"double2\":4.9E-324," + + "\"boolean2\":false" + + "}"; + Truth.assertThat(attributes.getJSONString()).isEqualTo(expectedJSON2); + } + @Test public void not_exceed_max_size() { Truth.assertThat(attributes.putString("key1", "value1")).isTrue();