diff --git a/packages/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/RNSentryJsonUtilsTest.kt b/packages/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/RNSentryJsonUtilsTest.kt new file mode 100644 index 0000000000..e48ba8637e --- /dev/null +++ b/packages/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/RNSentryJsonUtilsTest.kt @@ -0,0 +1,102 @@ +package io.sentry.rnsentryandroidtester + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.facebook.react.bridge.JavaOnlyMap +import com.facebook.soloader.SoLoader +import io.sentry.react.RNSentryJsonUtils +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class RNSentryJsonUtilsTest { + @Before + fun setUp() { + val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + SoLoader.init(context, false) + } + + @Test + fun testJsonObjectToReadableMap() { + val json = + JSONObject().apply { + put("stringKey", "stringValue") + put("booleanKey", true) + put("intKey", 123) + } + + val result = RNSentryJsonUtils.jsonObjectToReadableMap(json) + + assertNotNull(result) + assertTrue(result is JavaOnlyMap) + assertEquals("stringValue", result?.getString("stringKey")) + assertEquals(true, result?.getBoolean("booleanKey")) + assertEquals(123, result?.getInt("intKey")) + } + + @Test + fun testNestedJsonObjectToReadableMap() { + val json = + JSONObject().apply { + put("stringKey", "stringValue") + put("booleanKey", true) + put("intKey", 123) + put( + "nestedKey", + JSONObject().apply { + put("nestedStringKey", "nestedStringValue") + put("nestedBooleanKey", false) + put( + "deepNestedArrayKey", + JSONArray().apply { + put("deepNestedArrayValue") + }, + ) + }, + ) + put( + "arrayKey", + JSONArray().apply { + put("arrayStringValue") + put(789) + put( + JSONObject().apply { + put("deepNestedStringKey", "deepNestedStringValue") + put("deepNestedBooleanKey", false) + }, + ) + }, + ) + } + + val result = RNSentryJsonUtils.jsonObjectToReadableMap(json) + + assertNotNull(result) + assertTrue(result is JavaOnlyMap) + assertEquals("stringValue", result?.getString("stringKey")) + assertEquals(true, result?.getBoolean("booleanKey")) + assertEquals(123, result?.getInt("intKey")) + val nested = result?.getMap("nestedKey") + assertNotNull(nested) + assertEquals("nestedStringValue", nested?.getString("nestedStringKey")) + assertEquals(false, nested?.getBoolean("nestedBooleanKey")) + val deepNestedArray = nested?.getArray("deepNestedArrayKey") + assertNotNull(deepNestedArray) + assertEquals("deepNestedArrayValue", deepNestedArray?.getString(0)) + val array = result?.getArray("arrayKey") + assertNotNull(array) + assertEquals("arrayStringValue", array?.getString(0)) + assertEquals(789, array?.getInt(1)) + val deepNested = array?.getMap(2) + assertNotNull(deepNested) + assertEquals("deepNestedStringValue", deepNested?.getString("deepNestedStringKey")) + assertEquals(false, deepNested?.getBoolean("deepNestedBooleanKey")) + } +} diff --git a/packages/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/RNSentryMapConverterTest.kt b/packages/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/RNSentryMapConverterTest.kt index 128dc187ba..a4af032406 100644 --- a/packages/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/RNSentryMapConverterTest.kt +++ b/packages/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/rnsentryandroidtester/RNSentryMapConverterTest.kt @@ -8,11 +8,8 @@ import com.facebook.react.bridge.JavaOnlyArray import com.facebook.react.bridge.JavaOnlyMap import com.facebook.soloader.SoLoader import io.sentry.react.RNSentryMapConverter -import org.json.JSONObject import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -700,22 +697,4 @@ class MapConverterTest { assertEquals(actual, expectedMap1) } - - @Test - fun testJsonObjectToReadableMap() { - val json = - JSONObject().apply { - put("stringKey", "stringValue") - put("booleanKey", true) - put("intKey", 123) - } - - val result = RNSentryMapConverter.jsonObjectToReadableMap(json) - - assertNotNull(result) - assertTrue(result is JavaOnlyMap) - assertEquals("stringValue", result.getString("stringKey")) - assertEquals(true, result.getBoolean("booleanKey")) - assertEquals(123, result.getInt("intKey")) - } } diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryJsonUtils.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryJsonUtils.java new file mode 100644 index 0000000000..ecd634b243 --- /dev/null +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryJsonUtils.java @@ -0,0 +1,110 @@ +package io.sentry.react; + +import android.content.Context; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; +import io.sentry.ILogger; +import io.sentry.SentryLevel; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public final class RNSentryJsonUtils { + private RNSentryJsonUtils() { + throw new AssertionError("Utility class should not be instantiated"); + } + + /** + * Read the configuration file in the Android assets folder and return the options as a + * JSONObject. + * + * @param context Android Context + * @param fileName configuration file name + * @param logger Sentry logger + * @return JSONObject with the configuration options + */ + public static @Nullable JSONObject getOptionsFromConfigurationFile( + @NotNull Context context, @NotNull String fileName, @NotNull ILogger logger) { + try (InputStream inputStream = context.getAssets().open(fileName); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + + StringBuilder stringBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + } + String configFileContent = stringBuilder.toString(); + return new JSONObject(configFileContent); + + } catch (Exception e) { + logger.log( + SentryLevel.ERROR, + "Failed to read configuration file. Please make sure " + + fileName + + " exists in the root of your project.", + e); + return null; + } + } + + private static @NotNull Map jsonObjectToMap(@NotNull JSONObject jsonObject) { + Map map = new HashMap<>(); + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = null; + try { + value = jsonObject.get(key); + } catch (JSONException e) { + throw new RuntimeException(e); + } + map.put(key, convertValue(value)); + } + return map; + } + + private static @NotNull List jsonArrayToList(@NotNull JSONArray jsonArray) { + List list = new ArrayList<>(); + + for (int i = 0; i < jsonArray.length(); i++) { + Object value = jsonArray.opt(i); + list.add(convertValue(value)); + } + + return list; + } + + private static @Nullable Object convertValue(@Nullable Object value) { + if (value instanceof JSONObject) { + return jsonObjectToMap((JSONObject) value); + } else if (value instanceof JSONArray) { + return jsonArrayToList((JSONArray) value); + } else { + return value; // Primitive type or null + } + } + + /** + * Convert a JSONObject to a ReadableMap + * + * @param jsonObject JSONObject to convert + * @return ReadableMap with the same data as the JSONObject + */ + public static @Nullable ReadableMap jsonObjectToReadableMap(@Nullable JSONObject jsonObject) { + if (jsonObject == null) { + return null; + } + Map map = jsonObjectToMap(jsonObject); + return (WritableMap) RNSentryMapConverter.convertToJavaWritable(map); + } +} diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryMapConverter.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryMapConverter.java index ceb5a1102b..2d5091ac18 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryMapConverter.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryMapConverter.java @@ -12,14 +12,10 @@ import io.sentry.android.core.AndroidLogger; import java.math.BigDecimal; import java.math.BigInteger; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.json.JSONException; -import org.json.JSONObject; public final class RNSentryMapConverter { public static final String NAME = "RNSentry.MapConverter"; @@ -202,25 +198,4 @@ private static void addValueToWritableMap(WritableMap writableMap, String key, O logger.log(SentryLevel.ERROR, "Could not convert object" + value); } } - - public static ReadableMap jsonObjectToReadableMap(JSONObject jsonObject) { - Map map = jsonObjectToMap(jsonObject); - return (WritableMap) convertToJavaWritable(map); - } - - private static Map jsonObjectToMap(JSONObject jsonObject) { - Map map = new HashMap<>(); - Iterator keys = jsonObject.keys(); - while (keys.hasNext()) { - String key = keys.next(); - Object value = null; - try { - value = jsonObject.get(key); - } catch (JSONException e) { - throw new RuntimeException(e); - } - map.put(key, value); - } - return map; - } } diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentrySDK.java b/packages/core/android/src/main/java/io/sentry/react/RNSentrySDK.java index 84b46138e2..5949b85c5f 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentrySDK.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentrySDK.java @@ -7,9 +7,6 @@ import io.sentry.SentryLevel; import io.sentry.android.core.AndroidLogger; import io.sentry.android.core.SentryAndroidOptions; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; import org.jetbrains.annotations.NotNull; import org.json.JSONObject; @@ -33,8 +30,9 @@ public static void init( @NotNull final Context context, @NotNull Sentry.OptionsConfiguration configuration) { try { - JSONObject jsonObject = getOptionsFromConfigurationFile(context); - ReadableMap rnOptions = RNSentryMapConverter.jsonObjectToReadableMap(jsonObject); + JSONObject jsonObject = + RNSentryJsonUtils.getOptionsFromConfigurationFile(context, CONFIGURATION_FILE, logger); + ReadableMap rnOptions = RNSentryJsonUtils.jsonObjectToReadableMap(jsonObject); RNSentryStart.startWithOptions(context, rnOptions, configuration, null, logger); } catch (Exception e) { logger.log( @@ -51,27 +49,4 @@ public static void init( public static void init(@NotNull final Context context) { init(context, options -> {}); } - - private static JSONObject getOptionsFromConfigurationFile(Context context) { - try (InputStream inputStream = context.getAssets().open(CONFIGURATION_FILE); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - - StringBuilder stringBuilder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - } - String configFileContent = stringBuilder.toString(); - return new JSONObject(configFileContent); - - } catch (Exception e) { - logger.log( - SentryLevel.ERROR, - "Failed to read configuration file. Please make sure " - + CONFIGURATION_FILE - + " exists in the root of your project.", - e); - return null; - } - } }