From 99d9f56156f50b73cfa24bf3370bc4036ddb3a6c Mon Sep 17 00:00:00 2001 From: Karen Tamayo Date: Wed, 21 Aug 2024 11:49:02 -0700 Subject: [PATCH] Android update: Add check for multiple Tealium instances during initialization (#177) * Android update: Add check for multiple Tealium instances during initialization --- CHANGELOG.md | 3 + example/ios/Podfile.lock | 8 +- npm-package/android/build.gradle | 2 +- .../java/com/tealium/react/TealiumReact.kt | 6 +- .../com/tealium/react/TealiumReactTests.kt | 91 ++++++++++++++++++- npm-package/index.js | 2 +- npm-package/package.json | 2 +- 7 files changed, 104 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7e72ae..e65b210b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ [Full documentation](https://docs.tealium.com/platforms/react-native/install/) +- 2.4.2 + - Android fix: added check during initialization to ensure only a single instance of Tealium is running. Pre-existing instances are shutdown. + - 2.4.1 - Update Tealium dependencies to the latest on both platforms - Android-Kotlin: Core 1.6.0, Tag Management 1.2.3, Remote Commands 1.4.0 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index bd1435a9..5f3aeda1 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -492,7 +492,7 @@ PODS: - tealium-react-native-swift (~> 2.4) - tealium-swift/Core (~> 2.12) - TealiumFirebase (~> 3.2) - - tealium-react-native (2.4.1): + - tealium-react-native (2.4.2): - React-Core - tealium-swift/Collect (~> 2.13) - tealium-swift/Core (~> 2.13) @@ -512,7 +512,7 @@ PODS: - tealium-react-native-swift (~> 2.4) - tealium-swift/Core (~> 2.12) - tealium-swift/Location (~> 2.12) - - tealium-react-native-swift (2.4.1): + - tealium-react-native-swift (2.4.2): - React-Core - tealium-react-native - tealium-swift/Collect (~> 2.13) @@ -796,10 +796,10 @@ SPEC CHECKSUMS: tealium-react-attribution: 5db8c4ccf60c0fea4790afc5a47537d4a198592c tealium-react-braze: f72256073bf99902dbfbfcd875399cd0fac61796 tealium-react-firebase: 44bc3f5f8294a0f9fc2fba5ebd109bde31a141e5 - tealium-react-native: c3cc2065e6fc6f16be13e02a50726a56db3fb8cf + tealium-react-native: c28c9edf54a59a261d910de52923cac338299d9a tealium-react-native-crash-reporter: b6bbb018f2d3fc1c27a0423aca58c33820395d41 tealium-react-native-location: 7f275669c9434c0877e8ba20af06ac37c003657f - tealium-react-native-swift: 6b483c1f6fc082e23384ec4d36f0fbb05f5b3dd2 + tealium-react-native-swift: a846102e391e9ae6fa3bd87331c078acf9d306d8 tealium-swift: f2a1d022bae58151c7ff31d2a063008cfe6463d8 TealiumAdjust: 7a8c033c93f5aaf306ebecaa2e575a5958e55e87 TealiumAdobeVisitorAPI: dd487f3e130829a15b9cdc7de7b2841a200214e2 diff --git a/npm-package/android/build.gradle b/npm-package/android/build.gradle index 3097c6a6..10eb2e56 100644 --- a/npm-package/android/build.gradle +++ b/npm-package/android/build.gradle @@ -43,7 +43,7 @@ buildscript { } } -version = "2.4.1" +version = "2.4.2" android { compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION) buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION) diff --git a/npm-package/android/src/main/java/com/tealium/react/TealiumReact.kt b/npm-package/android/src/main/java/com/tealium/react/TealiumReact.kt index 180961af..75254eac 100644 --- a/npm-package/android/src/main/java/com/tealium/react/TealiumReact.kt +++ b/npm-package/android/src/main/java/com/tealium/react/TealiumReact.kt @@ -53,6 +53,10 @@ class TealiumReact(private val reactContext: ReactApplicationContext) : ReactCon @ReactMethod fun initialize(configMap: ReadableMap, callback: Callback?) { getApplication()?.let { app -> + if (tealium != null) { + terminateInstance() + } + configMap.toTealiumConfig(app)?.let { config -> optionalModules.forEach { module -> try { @@ -100,7 +104,7 @@ class TealiumReact(private val reactContext: ReactApplicationContext) : ReactCon return app } - private fun createRemoteCommands(commands: ReadableArray) { + internal fun createRemoteCommands(commands: ReadableArray) { for (i in 0 until commands.size()) { val cmd = commands.getMap(i) if (cmd is ReadableMap) { diff --git a/npm-package/android/src/test/java/com/tealium/react/TealiumReactTests.kt b/npm-package/android/src/test/java/com/tealium/react/TealiumReactTests.kt index 4ab59862..cb60a2c9 100644 --- a/npm-package/android/src/test/java/com/tealium/react/TealiumReactTests.kt +++ b/npm-package/android/src/test/java/com/tealium/react/TealiumReactTests.kt @@ -5,6 +5,7 @@ import android.app.Application import android.util.Log import com.facebook.react.bridge.* import com.tealium.core.Tealium +import com.tealium.core.TealiumConfig import com.tealium.core.consent.ConsentCategory import com.tealium.core.consent.ConsentManager import com.tealium.core.consent.ConsentStatus @@ -22,6 +23,8 @@ import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.RelaxedMockK import org.json.JSONArray import org.json.JSONObject +import org.junit.After +import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before @@ -50,6 +53,12 @@ class TealiumReactTests { @RelaxedMockK lateinit var mockTealium: Tealium + @RelaxedMockK + lateinit var mockTealium2: Tealium + + @RelaxedMockK + lateinit var mockTealium3: Tealium + @RelaxedMockK lateinit var mockRemoteCommandDispatcher: RemoteCommandDispatcher @@ -81,10 +90,19 @@ class TealiumReactTests { mockkObject(Tealium) val callback = slot Unit>() every { Tealium.create(any(), any(), capture(callback)) } answers { - callback.captured(mockTealium) + callback.captured.invoke(mockTealium) mockTealium + } andThenAnswer { + callback.captured.invoke(mockTealium2) + mockTealium2 + } andThenAnswer { + callback.captured.invoke(mockTealium3) + mockTealium3 } + every { mockTealium.remoteCommands } returns mockRemoteCommandDispatcher + every { mockTealium2.remoteCommands } returns mockRemoteCommandDispatcher + every { mockTealium3.remoteCommands } returns mockRemoteCommandDispatcher every { mockRemoteCommandDispatcher.add(any(), any(), any()) } just Runs minimalConfig = JavaOnlyMap().apply { @@ -93,7 +111,6 @@ class TealiumReactTests { } tealiumReact = TealiumReact(mockReactApplicationContext) - tealiumReact.initialize(minimalConfig, null) } @Test @@ -105,6 +122,7 @@ class TealiumReactTests { @Test fun initialize_UsesMain_InstanceName() { + tealiumReact.initialize(minimalConfig, null) // init called in @Before verify { Tealium.create(INSTANCE_NAME, match { @@ -114,6 +132,33 @@ class TealiumReactTests { } } + @Test + fun initialize_SecondInstance_TerminatesCurrentInstance() { + tealiumReact.initialize(minimalConfig, null) + verify { + Tealium.create(INSTANCE_NAME, match { + it.accountName == "account" + && it.profileName == "profile" + }, any()) + } + + val config = JavaOnlyMap().apply { + putString(KEY_CONFIG_ACCOUNT, "test") + putString(KEY_CONFIG_PROFILE, "testProfile") + } + tealiumReact.initialize(config, callback = null) + + verify { + Tealium.destroy(any()) + Tealium.create(INSTANCE_NAME, match { + it.accountName == "test" + && it.profileName == "testProfile" + }, any()) + } + + assertEquals(mockTealium2, tealiumReact.tealium) + } + @Test fun initialize_CreatesLocalRemoteCommand_WhenCallbackSupplied() { val remoteCommandsArray: WritableArray = JavaOnlyArray() @@ -124,6 +169,8 @@ class TealiumReactTests { minimalConfig.putArray(KEY_REMOTE_COMMANDS_CONFIG, remoteCommandsArray) tealiumReact.initialize(minimalConfig, null) + minimalConfig.safeGetArray(KEY_REMOTE_COMMANDS_CONFIG) + ?.let { tealiumReact.createRemoteCommands(it) } verify(exactly = 1) { mockRemoteCommandDispatcher.add(match { @@ -144,6 +191,9 @@ class TealiumReactTests { minimalConfig.putArray(KEY_REMOTE_COMMANDS_CONFIG, remoteCommandsArray) tealiumReact.initialize(minimalConfig, null) + minimalConfig.safeGetArray(KEY_REMOTE_COMMANDS_CONFIG)?.let { + tealiumReact.createRemoteCommands(it) + } verify(exactly = 1) { mockRemoteCommandDispatcher.add(match { @@ -193,6 +243,9 @@ class TealiumReactTests { // register factory tealiumReact.registerRemoteCommandFactory(TestRemoteCommandFactory("factory_command")) tealiumReact.initialize(minimalConfig, null) + minimalConfig.safeGetArray(KEY_REMOTE_COMMANDS_CONFIG)?.let { + tealiumReact.createRemoteCommands(it) + } verify(exactly = 1) { mockRemoteCommandDispatcher.add(match { @@ -265,6 +318,7 @@ class TealiumReactTests { @Test fun track_CallsTrack() { + tealiumReact.initialize(minimalConfig, null) val eventMap = JavaOnlyMap().apply { putString(KEY_TRACK_EVENT_TYPE, "event") putString(KEY_TRACK_EVENT_NAME, "myEvent") @@ -291,6 +345,7 @@ class TealiumReactTests { @Test fun addToDataLayer_String() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -306,6 +361,7 @@ class TealiumReactTests { @Test fun addToDataLayer_Int() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -322,6 +378,7 @@ class TealiumReactTests { @Test fun addToDataLayer_Double() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -337,6 +394,7 @@ class TealiumReactTests { @Test fun addToDataLayer_Boolean() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -352,6 +410,7 @@ class TealiumReactTests { @Test fun addToDataLayer_StringArray() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -370,6 +429,7 @@ class TealiumReactTests { @Test fun addToDataLayer_IntArray() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -389,6 +449,7 @@ class TealiumReactTests { @Test fun addToDataLayer_DoubleArray() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -407,6 +468,7 @@ class TealiumReactTests { @Test fun addToDataLayer_BooleanArray() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -425,6 +487,7 @@ class TealiumReactTests { @Test fun addToDataLayer_ObjectArray() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -456,6 +519,7 @@ class TealiumReactTests { @Test fun addToDataLayer_MixedArray() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -486,6 +550,7 @@ class TealiumReactTests { @Test fun addToDataLayer_Object() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -507,6 +572,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_String() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -521,6 +587,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_Int() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -535,6 +602,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_Double() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -549,6 +617,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_Boolean() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -563,6 +632,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_StringArray() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -581,6 +651,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_IntArray() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -600,6 +671,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_DoubleArray() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -618,6 +690,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_BooleanArray() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -636,6 +709,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_ObjectArray() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -660,6 +734,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_MixedArray() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -685,6 +760,7 @@ class TealiumReactTests { @Test fun getFromDataLayer_Object() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -731,6 +807,7 @@ class TealiumReactTests { @Test fun removeFromDataLayer_CallsRemove_ForEachKey() { + tealiumReact.initialize(minimalConfig, null) val dataLayer: DataLayer = mockk(relaxed = true) every { mockTealium.dataLayer } returns dataLayer @@ -748,6 +825,7 @@ class TealiumReactTests { @Test fun gatherTrackData_Successful() { + tealiumReact.initialize(minimalConfig, null) val callback: Callback = mockk(relaxed = true) every { mockTealium.gatherTrackData() } returns mapOf( "tealium_account" to "account", @@ -767,6 +845,7 @@ class TealiumReactTests { @Test fun setConsentStatus_AlwaysSetsStatus() { + tealiumReact.initialize(minimalConfig, null) val consentManager: ConsentManager = mockk(relaxed = true) every { mockTealium.consentManager } returns consentManager @@ -787,6 +866,7 @@ class TealiumReactTests { @Test fun setConsentCategories_SetsCategoryList() { + tealiumReact.initialize(minimalConfig, null) val consentManager: ConsentManager = mockk(relaxed = true) every { mockTealium.consentManager } returns consentManager @@ -820,6 +900,7 @@ class TealiumReactTests { @Test fun getConsentCategories_CallsCallback() { + tealiumReact.initialize(minimalConfig, null) val consentManager: ConsentManager = mockk(relaxed = true) every { mockTealium.consentManager } returns consentManager every { consentManager.userConsentCategories } returns setOf(ConsentCategory.AFFILIATES, ConsentCategory.ANALYTICS) @@ -837,6 +918,7 @@ class TealiumReactTests { @Test fun addRemoteCommand_AddsRemoteCommandListener() { + tealiumReact.initialize(minimalConfig, null) val remoteCommandDispatcher: RemoteCommandDispatcher = mockk(relaxed = true) every { mockTealium.remoteCommands } returns remoteCommandDispatcher every { remoteCommandDispatcher.add(any()) } just Runs @@ -853,6 +935,7 @@ class TealiumReactTests { @Test fun removeRemoteCommand_RemovesById() { + tealiumReact.initialize(minimalConfig, null) val remoteCommandDispatcher: RemoteCommandDispatcher = mockk(relaxed = true) every { mockTealium.remoteCommands } returns remoteCommandDispatcher every { remoteCommandDispatcher.remove(any()) } just Runs @@ -866,6 +949,7 @@ class TealiumReactTests { @Test fun joinTrace_JoinsProvidedTraceId() { + tealiumReact.initialize(minimalConfig, null) every { mockTealium.joinTrace(any()) } just Runs tealiumReact.joinTrace("id") @@ -877,6 +961,7 @@ class TealiumReactTests { @Test fun leaveTrace_LeavesTrace() { + tealiumReact.initialize(minimalConfig, null) every { mockTealium.leaveTrace() } just Runs tealiumReact.leaveTrace() @@ -888,6 +973,7 @@ class TealiumReactTests { @Test fun getVisitorId_CallsCallback() { + tealiumReact.initialize(minimalConfig, null) every { mockTealium.visitorId } returns "visitor123" tealiumReact.getVisitorId(mockCallback) @@ -910,6 +996,7 @@ class TealiumReactTests { @Test fun getSessionId_CallsCallback() { + tealiumReact.initialize(minimalConfig, null) every { mockTealium.session.id } returns 12345 tealiumReact.getSessionId(mockCallback) diff --git a/npm-package/index.js b/npm-package/index.js index 90890667..d525a53a 100644 --- a/npm-package/index.js +++ b/npm-package/index.js @@ -17,7 +17,7 @@ export default class Tealium { }); } TealiumWrapper.initialize(config, callback || (response => {})); - TealiumWrapper.addToDataLayer({'plugin_name': 'Tealium-ReactNative', 'plugin_version': '2.4.1'}, Expiry.forever); + TealiumWrapper.addToDataLayer({'plugin_name': 'Tealium-ReactNative', 'plugin_version': '2.4.2'}, Expiry.forever); if (config["dispatchers"].includes(Dispatchers.RemoteCommands)) { this.setRemoteCommandListener(); } diff --git a/npm-package/package.json b/npm-package/package.json index 6090bbe8..5f9c3096 100644 --- a/npm-package/package.json +++ b/npm-package/package.json @@ -1,7 +1,7 @@ { "name": "tealium-react-native", "title": "Tealium React Native", - "version": "2.4.1", + "version": "2.4.2", "description": "A native module for using Tealium's Kotlin and Swift libraries.", "main": "index.js", "types": "*.ts",