From 6d48ad22235078db4cdc9de38c4dccaacc5a3cc1 Mon Sep 17 00:00:00 2001 From: Kevin Lind <40409666+kevinlind@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:44:48 -0700 Subject: [PATCH 1/2] Fix bug in Places parsing where only String types are assumed (#89) * Add functional test using full map of Places shared state to validate type casting/parsing. * Read Places shared state as Map of Objects. Read Places shared state as map of String Objects instead of String String as retrieval fails if shared state contains types other than String. Places shared state may contain various values types such as numbers, doubles, and maps. * Fix formatting in functional tests. * Add unit tests to validate AnalyticsState parsing of Places shared state. --- .../analytics/internal/AnalyticsState.kt | 6 +- .../test/AnalyticsTrackPlacesTests.kt | 97 ++++++++++++++++++- .../analytics/internal/AnalyticsStateTests.kt | 77 +++++++++++++++ 3 files changed, 175 insertions(+), 5 deletions(-) diff --git a/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsState.kt b/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsState.kt index 2144cd1..b790051 100644 --- a/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsState.kt +++ b/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsState.kt @@ -222,17 +222,17 @@ class AnalyticsState { */ private fun extractPlacesInfo(placesInfo: Map) { val placesContextData = DataReader.optTypedMap( - String::class.java, + Object::class.java, placesInfo, AnalyticsConstants.EventDataKeys.Places.CURRENT_POI, null ) ?: return - val regionId = placesContextData[AnalyticsConstants.EventDataKeys.Places.REGION_ID] + val regionId = placesContextData[AnalyticsConstants.EventDataKeys.Places.REGION_ID] as? String if (!StringUtils.isNullOrEmpty(regionId)) { defaultData[AnalyticsConstants.ContextDataKeys.REGION_ID] = regionId ?: "" } - val regionName = placesContextData[AnalyticsConstants.EventDataKeys.Places.REGION_NAME] + val regionName = placesContextData[AnalyticsConstants.EventDataKeys.Places.REGION_NAME] as? String if (!StringUtils.isNullOrEmpty(regionName)) { defaultData[AnalyticsConstants.ContextDataKeys.REGION_NAME] = regionName ?: "" diff --git a/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/function/test/AnalyticsTrackPlacesTests.kt b/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/function/test/AnalyticsTrackPlacesTests.kt index f4a2001..561ae74 100644 --- a/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/function/test/AnalyticsTrackPlacesTests.kt +++ b/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/function/test/AnalyticsTrackPlacesTests.kt @@ -97,7 +97,100 @@ internal class AnalyticsTrackPlacesTests : AnalyticsFunctionalTestBase() { "a.loc.poi.id" to "myRegionId", "a.loc.poi" to "myRegionName" ) - Assert.assertTrue(expectedContextData == contextDataMap) + Assert.assertEquals(expectedContextData, contextDataMap) + Assert.assertEquals(expectedVars.size, varMap.size) + Assert.assertEquals(expectedVars, varMap) + } + + @Test(timeout = 10000) + fun `analytics hit contains current poi places data`() { + val countDownLatch = CountDownLatch(1) + var varMap: Map = emptyMap() + var contextDataMap: Map = emptyMap() + monitorNetwork { request -> + if (request.url.startsWith("https://test.com/b/ss/rsid/0/")) { + val body = URLDecoder.decode(String(request.body), "UTF-8") + varMap = extractQueryParamsFrom(body) + contextDataMap = extractContextDataFrom(body) + countDownLatch.countDown() + } + } + + val analyticsExtension = initializeAnalyticsExtensionWithPreset( + mapOf( + "analytics.server" to "test.com", + "analytics.rsids" to "rsid", + "global.privacy" to "optedin", + "experienceCloud.org" to "orgid", + "analytics.batchLimit" to 0, + "analytics.offlineEnabled" to true, + "analytics.backdatePreviousSessionInfo" to true, + "analytics.launchHitDelay" to 1 + ), + defaultIdentity() + ) + + // Test with full shared state to validate parsing/casting of types + updateMockedSharedState( + "com.adobe.module.places", + mapOf( + "currentpoi" to mapOf( + "regionid" to "99306680-a0e5-49f1-b0eb-c52c6e05ce01", + "useriswithin" to true, + "latitude" to 37.3309257, + "libraryid" to "311cbfb0-ac5e-436a-b22d-4a917426880d", + "regionname" to "Adobe 100", + "weight" to 1, + "regionmetadata" to mapOf( + "country" to "Adobe 100", + "city" to "Adobe 100", + "street" to "Adobe 100", + "state" to "Adobe 100", + "category" to "Adobe 100" + ), + "radius" to 100, + "longitude" to -121.8939791 + ) + ) + ) + + val trackEvent = Event.Builder( + "track event", + EventType.GENERIC_TRACK, + EventSource.REQUEST_CONTENT + ).setEventData( + mapOf( + "action" to "testActionName", + "contextdata" to mapOf( + "k1" to "v1", + "k2" to "v2" + ) + ) + ).build() + + analyticsExtension.handleIncomingEvent(trackEvent) + + countDownLatch.await() + val expectedVars: Map = mapOf( + "ndh" to "1", + "ce" to "UTF-8", + "cp" to "foreground", + "pev2" to "AMACTION:testActionName", + "pe" to "lnk_o", + "mid" to "mid", + "aamb" to "blob", + "aamlh" to "lochint", + "t" to TimeZoneHelper.TIMESTAMP_TIMEZONE_OFFSET, + "ts" to trackEvent.timestampInSeconds.toString() + ) + val expectedContextData: Map = mapOf( + "k1" to "v1", + "k2" to "v2", + "a.action" to "testActionName", + "a.loc.poi.id" to "99306680-a0e5-49f1-b0eb-c52c6e05ce01", + "a.loc.poi" to "Adobe 100" + ) + Assert.assertEquals(expectedContextData, contextDataMap) Assert.assertEquals(expectedVars.size, varMap.size) Assert.assertEquals(expectedVars, varMap) } @@ -184,7 +277,7 @@ internal class AnalyticsTrackPlacesTests : AnalyticsFunctionalTestBase() { "a.loc.poi.id" to "myRegionId2", "a.loc.poi" to "myRegionName2" ) - Assert.assertTrue(expectedContextData == contextDataMap) + Assert.assertEquals(expectedContextData, contextDataMap) Assert.assertEquals(expectedVars.size, varMap.size) Assert.assertEquals(expectedVars, varMap) } diff --git a/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsStateTests.kt b/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsStateTests.kt index 36be81e..93c4f81 100644 --- a/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsStateTests.kt +++ b/code/analytics/src/test/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsStateTests.kt @@ -159,6 +159,41 @@ class AnalyticsStateTests { ) } + @Test + fun testExtractPlacesInfo_returnsRegionNameAndID_whenStateContainsOtherTypes() { + state.update( + mapOf( + PLACES_SHARED_STATE to mapOf( + "currentpoi" to mapOf( + "regionid" to "99306680-a0e5-49f1-b0eb-c52c6e05ce01", + "useriswithin" to true, + "latitude" to 37.3309257, + "libraryid" to "311cbfb0-ac5e-436a-b22d-4a917426880d", + "regionname" to "Adobe 100", + "weight" to 1, + "regionmetadata" to mapOf( + "country" to "Adobe 100", + "city" to "Adobe 100", + "street" to "Adobe 100", + "state" to "Adobe 100", + "category" to "Adobe 100" + ), + "radius" to 100, + "longitude" to -121.8939791 + ) + ) + ) + ) + assertEquals( + "99306680-a0e5-49f1-b0eb-c52c6e05ce01", + state.defaultData[AnalyticsConstants.ContextDataKeys.REGION_ID] + ) + assertEquals( + "Adobe 100", + state.defaultData[AnalyticsConstants.ContextDataKeys.REGION_NAME] + ) + } + @Test fun testExtractPlacesInfo_returnsDefaultValues_when_empty() { state.update( @@ -183,6 +218,48 @@ class AnalyticsStateTests { assertTrue(state.defaultData.isEmpty()) } + @Test + fun testExtractPlacesInfo_returnsDefaultValuesAndDoesNotCrash_when_regionname_invalidType() { + state.update( + mapOf( + PLACES_SHARED_STATE to mapOf( + "currentpoi" to mapOf( + "regionid" to "sampleRegionId", + "regionname" to true + ) + ) + ) + ) + assertEquals( + "sampleRegionId", + state.defaultData[AnalyticsConstants.ContextDataKeys.REGION_ID] + ) + assertNull( + state.defaultData[AnalyticsConstants.ContextDataKeys.REGION_NAME] + ) + } + + @Test + fun testExtractPlacesInfo_returnsDefaultValuesAndDoesNotCrash_when_regionid_invalidType() { + state.update( + mapOf( + PLACES_SHARED_STATE to mapOf( + "currentpoi" to mapOf( + "regionid" to 123, + "regionname" to "sampleRegionName" + ) + ) + ) + ) + assertEquals( + "sampleRegionName", + state.defaultData[AnalyticsConstants.ContextDataKeys.REGION_NAME] + ) + assertNull( + state.defaultData[AnalyticsConstants.ContextDataKeys.REGION_ID] + ) + } + @Test fun testExtractPlacesInfo_shouldNotIncludeNullValues() { state.update( From 5ce490e21fcbe9554ad3e191a95fd2e20f06d894 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:00:00 -0700 Subject: [PATCH 2/2] Updating version to 3.0.1 (#90) * Updating version to 3.0.1. * Update version to 3.0.1 in Analyitcs.java --------- Co-authored-by: kevinlind Co-authored-by: Kevin Lind --- .../marketing/mobile/analytics/internal/AnalyticsConstants.kt | 2 +- .../src/phone/java/com/adobe/marketing/mobile/Analytics.java | 2 +- code/gradle.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsConstants.kt b/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsConstants.kt index 6497657..3550e04 100644 --- a/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsConstants.kt +++ b/code/analytics/src/main/java/com/adobe/marketing/mobile/analytics/internal/AnalyticsConstants.kt @@ -18,7 +18,7 @@ internal object AnalyticsConstants { const val EXTENSION_NAME = "com.adobe.module.analytics" const val FRIENDLY_NAME = "Analytics" - const val EXTENSION_VERSION = "3.0.0" + const val EXTENSION_VERSION = "3.0.1" const val DATASTORE_NAME = "AnalyticsDataStorage" const val DATA_QUEUE_NAME = EXTENSION_NAME const val REORDER_QUEUE_NAME = "com.adobe.module.analyticsreorderqueue" diff --git a/code/analytics/src/phone/java/com/adobe/marketing/mobile/Analytics.java b/code/analytics/src/phone/java/com/adobe/marketing/mobile/Analytics.java index 15c5e6b..56840bd 100644 --- a/code/analytics/src/phone/java/com/adobe/marketing/mobile/Analytics.java +++ b/code/analytics/src/phone/java/com/adobe/marketing/mobile/Analytics.java @@ -21,7 +21,7 @@ public class Analytics { private static final String LOG_TAG = "Analytics"; - private static final String EXTENSION_VERSION = "3.0.0"; + private static final String EXTENSION_VERSION = "3.0.1"; // The constants for EventData keys private static final String ANALYTICS_ID = "aid"; private static final String GET_QUEUE_SIZE = "getqueuesize"; diff --git a/code/gradle.properties b/code/gradle.properties index 66ed9f3..84db38b 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -17,7 +17,7 @@ org.gradle.caching=true android.useAndroidX=true moduleName=analytics -moduleVersion=3.0.0 +moduleVersion=3.0.1 mavenRepoName=AdobeMobileAnalyticsSdk mavenRepoDescription=Android Analytics Extension for Adobe Mobile Marketing