diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index eedd06cbc..a74cf7260 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -110,7 +110,6 @@ public class MParticle { protected boolean locationTrackingEnabled = false; @NonNull protected Internal mInternal = new Internal(); - protected Rokt rokt; private IdentityStateListener mDeferredModifyPushRegistrationListener; @NonNull private WrapperSdkVersion wrapperSdkVersion = new WrapperSdkVersion(WrapperSdk.WrapperNone, null); @@ -190,7 +189,6 @@ private static MParticle getInstance(@NonNull Context context, @NonNull MParticl instance = new MParticle(options); instance.mKitManager = new KitFrameworkWrapper(options.getContext(), instance.mMessageManager, instance.Internal().getConfigManager(), instance.Internal().getAppStateManager(), options); instance.mIdentityApi = new IdentityApi(options.getContext(), instance.mInternal.getAppStateManager(), instance.mMessageManager, instance.mConfigManager, instance.mKitManager, options.getOperatingSystem()); - instance.rokt = new Rokt(instance.mConfigManager, instance.mKitManager); // Check if we've switched workspaces on startup UploadSettings lastUploadSettings = instance.mConfigManager.getLastUploadSettings(); @@ -1128,11 +1126,6 @@ public void logNotificationOpened(@NonNull Intent intent) { public Internal Internal() { return mInternal; } - @NonNull - public Rokt Rokt() { - return rokt; - } - void refreshConfiguration() { Logger.debug("Refreshing configuration..."); mMessageManager.refreshConfiguration(); diff --git a/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java b/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java index 1065d4102..08be54515 100644 --- a/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java +++ b/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java @@ -457,6 +457,11 @@ public boolean onPushRegistration(String instanceId, String senderId) { return false; } + @Override + public boolean isEnabled() { + return mCoreCallbacks.isEnabled(); + } + @Override public boolean isKitActive(int kitId) { if (mKitManager != null) { @@ -662,15 +667,6 @@ public void reset() { } } - @Override - @Nullable - public RoktKitApi getRoktKitApi() { - if (mKitManager != null) { - return mKitManager.getRoktKitApi(); - } - return null; - } - @Override public void setWrapperSdkVersion(@NonNull WrapperSdkVersion wrapperSdkVersion) { if (mKitManager != null) { diff --git a/android-core/src/main/java/com/mparticle/internal/KitManager.java b/android-core/src/main/java/com/mparticle/internal/KitManager.java index b821bdd73..94b50fdbd 100644 --- a/android-core/src/main/java/com/mparticle/internal/KitManager.java +++ b/android-core/src/main/java/com/mparticle/internal/KitManager.java @@ -75,6 +75,8 @@ public interface KitManager { boolean onPushRegistration(String instanceId, String senderId); + boolean isEnabled(); + boolean isKitActive(int kitId); Object getKitInstance(int kitId); @@ -127,14 +129,6 @@ public interface KitManager { void reset(); - /** - * Get the RoktKitApi implementation if available. - * - * @return RoktKitApi instance or null if Rokt Kit is not configured or active - */ - @Nullable - RoktKitApi getRoktKitApi(); - void setWrapperSdkVersion(@NonNull WrapperSdkVersion wrapperSdkVersion); enum KitStatus { diff --git a/android-core/src/main/kotlin/com/mparticle/MpRoktEventCallback.kt b/android-core/src/main/kotlin/com/mparticle/MpRoktEventCallback.kt deleted file mode 100644 index 92318642c..000000000 --- a/android-core/src/main/kotlin/com/mparticle/MpRoktEventCallback.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.mparticle - -/** - * ### Optional callback events for when the view loads and unloads. - */ -interface MpRoktEventCallback { - /** - * onLoad Callback will be triggered immediately when the View displays. - */ - fun onLoad() - - /** - * onUnLoad Callback will be triggered if the View failed to show or it closed. - */ - fun onUnload(reason: UnloadReasons) - - /** - * onShouldShowLoadingIndicator callback will be triggered if View starts processing. - */ - fun onShouldShowLoadingIndicator() - - /** - * onShouldHideLoadingIndicator callback will be triggered if View ends processing. - */ - fun onShouldHideLoadingIndicator() -} - -/** - * Enum representing the reasons for unloading. - */ -enum class UnloadReasons { - /** - * Called when there are no offers to display so the view does not get loaded in. - */ - NO_OFFERS, - - /** - * View has been rendered and has been completed. - */ - FINISHED, - - /** - * Operation to fetch view took too long to resolve. - */ - TIMEOUT, - - /** - * Some error has occurred regarding the network. - */ - NETWORK_ERROR, - - /** - * View is empty. - */ - NO_WIDGET, - - /** - * Init request was not successful. - */ - INIT_FAILED, - - /** - * Placeholder string mismatch. - */ - UNKNOWN_PLACEHOLDER, - - /** - * Catch-all for all issues. - */ - UNKNOWN, - - ; - - companion object { - /** - * Returns the enum constant matching the provided string. - * If no match is found, UNKNOWN is returned. - * - * @param value the name of the enum constant to look up - * @return the corresponding UnloadReasons constant or UNKNOWN if no match is found - */ - fun from(value: String): UnloadReasons = try { - valueOf(value) - } catch (e: IllegalArgumentException) { - UNKNOWN - } - } -} diff --git a/android-core/src/main/kotlin/com/mparticle/RoktEvent.kt b/android-core/src/main/kotlin/com/mparticle/RoktEvent.kt deleted file mode 100644 index fa9d6aa85..000000000 --- a/android-core/src/main/kotlin/com/mparticle/RoktEvent.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.mparticle - -// RoktEvent interface for handling events from the Rokt SDK. -sealed interface RoktEvent { - /** - * ShowLoadingIndicator event will be triggered before SDK calls Rokt backend - */ - data object ShowLoadingIndicator : RoktEvent - - /** - * HideLoadingIndicator event will be triggered when SDK obtains a success or failure from - * Rokt backend - */ - data object HideLoadingIndicator : RoktEvent - - /** - * OfferEngagement event will be triggered if User engaged with the offer - * @param placementId - identifier for the placement emitting the event - */ - data class OfferEngagement(val placementId: String) : RoktEvent - - /** - * PositiveEngagement event will be triggered if User positively engaged with the offer - * @param placementId - identifier for the placement emitting the event - */ - data class PositiveEngagement(val placementId: String) : RoktEvent - - /** - * FirstPositiveEngagement event will be triggered when the user positively engaged with the offer first time - * @param placementId - identifier for the placement emitting the event - */ - data class FirstPositiveEngagement(val placementId: String) : RoktEvent - - /** - * PlacementInteractive event will be triggered when placement has been rendered and is interactable - * @param placementId - identifier for the placement emitting the event - */ - data class PlacementInteractive(val placementId: String) : RoktEvent - - /** - * PlacementReady event will be triggered when placement is ready to display but has not rendered content yet - * @param placementId - identifier for the placement emitting the event - */ - data class PlacementReady(val placementId: String) : RoktEvent - - /** - * PlacementClosed event will be triggered when placement closes by user - * @param placementId - identifier for the placement emitting the event - */ - data class PlacementClosed(val placementId: String) : RoktEvent - - /** - * PlacementCompleted event will be triggered when the offer progression moves to the end and no more - * offer to display - * @param placementId - identifier for the placement emitting the event - */ - data class PlacementCompleted(val placementId: String) : RoktEvent - - /** - * PlacementFailure event will be triggered when placement could not be displayed due to some failure - * @param placementId - optional identifier for the placement emitting the event - */ - data class PlacementFailure(val placementId: String? = null) : RoktEvent - - /** - * InitComplete event will be triggered when SDK has finished initialization - * @param success - true if init was successful - */ - data class InitComplete(val success: Boolean) : RoktEvent - - /** - * OpenUrl event will be triggered when user clicks on a link and the link target is set to Passthrough - * @param placementId - identifier for the placement emitting the event - * @param url - url to open - */ - data class OpenUrl(val placementId: String, val url: String) : RoktEvent - - /** - * CartItemInstantPurchase event will be triggered when the catalog item purchase is initiated - * by the user - * @property placementId The layout identifier. - * @property cartItemId The cart item identifier. - * @property catalogItemId The catalog item identifier. - * @property currency The currency used for the purchase. - * @property description The description of the cart item. - * @property linkedProductId The linked product identifier. - * @property totalPrice The total price of the cart item. - * @property quantity The quantity of the cart item. - * @property unitPrice The unit price of the cart item. - */ - data class CartItemInstantPurchase( - val placementId: String, - val cartItemId: String, - val catalogItemId: String, - val currency: String, - val description: String, - val linkedProductId: String, - val totalPrice: Double, - val quantity: Int, - val unitPrice: Double, - ) : RoktEvent -} diff --git a/android-core/src/main/kotlin/com/mparticle/internal/RoktKitApi.kt b/android-core/src/main/kotlin/com/mparticle/internal/RoktKitApi.kt deleted file mode 100644 index 68f9c628d..000000000 --- a/android-core/src/main/kotlin/com/mparticle/internal/RoktKitApi.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.mparticle.internal - -import android.graphics.Typeface -import com.mparticle.MpRoktEventCallback -import com.mparticle.RoktEvent -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig -import com.mparticle.rokt.RoktEmbeddedView -import kotlinx.coroutines.flow.Flow -import java.lang.ref.WeakReference - -/** - * Interface for Rokt Kit operations. - * - * Implementations of this interface are provided by the Rokt Kit when it is - * configured and active. Use [KitManager.getRoktKitApi] to obtain an instance. - */ -interface RoktKitApi { - /** - * Initiate a Rokt placement selection with the specified parameters. - * - * @param viewName The identifier for the placement view - * @param attributes User attributes to pass to Rokt - * @param mpRoktEventCallback Optional callback for Rokt events - * @param placeHolders Optional map of embedded view placeholders - * @param fontTypefaces Optional map of font typefaces - * @param config Optional Rokt configuration - * @param options Optional placement options - */ - fun selectPlacements( - viewName: String, - attributes: Map, - mpRoktEventCallback: MpRoktEventCallback?, - placeHolders: Map>?, - fontTypefaces: Map>?, - config: RoktConfig?, - options: PlacementOptions? = null, - ) - - /** - * Get a Flow of Rokt events for the specified identifier. - * - * @param identifier The placement identifier to listen for events - * @return A Flow emitting RoktEvent objects - */ - fun events(identifier: String): Flow - - /** - * Notify Rokt that a purchase has been finalized. - * - * @param placementId The placement identifier - * @param catalogItemId The catalog item identifier - * @param status Whether the purchase was successful - */ - fun purchaseFinalized(placementId: String, catalogItemId: String, status: Boolean) - - /** - * Close any active Rokt placements. - */ - fun close() - - /** - * Set the session id to use for the next execute call. - * - * This is useful for cases where you have a session id from a non-native integration, - * e.g. WebView, and you want the session to be consistent across integrations. - * - * **Note:** Empty strings are ignored and will not update the session. - * - * @param sessionId The session id to be set. Must be a non-empty string. - */ - fun setSessionId(sessionId: String) - - /** - * Get the session id to use within a non-native integration e.g. WebView. - * - * @return The session id or null if no session is present. - */ - fun getSessionId(): String? - - /** - * Prepare attributes asynchronously before executing a placement. - * - * @param attributes The attributes to prepare - */ - fun prepareAttributesAsync(attributes: Map) -} diff --git a/android-core/src/main/kotlin/com/mparticle/rokt/PlacementOptions.kt b/android-core/src/main/kotlin/com/mparticle/rokt/PlacementOptions.kt deleted file mode 100644 index c3f2a3417..000000000 --- a/android-core/src/main/kotlin/com/mparticle/rokt/PlacementOptions.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.mparticle.rokt - -data class PlacementOptions(val jointSdkSelectPlacements: Long, val dynamicPerformanceMarkers: Map = mapOf()) diff --git a/android-core/src/main/kotlin/com/mparticle/rokt/RoktConfig.kt b/android-core/src/main/kotlin/com/mparticle/rokt/RoktConfig.kt deleted file mode 100644 index 28488a0e1..000000000 --- a/android-core/src/main/kotlin/com/mparticle/rokt/RoktConfig.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.mparticle.rokt - -class RoktConfig private constructor(val colorMode: ColorMode?, val cacheConfig: CacheConfig?, val edgeToEdgeDisplay: Boolean) { - data class Builder( - private var colorMode: ColorMode? = null, - private var cacheConfig: CacheConfig? = null, - private var edgeToEdgeDisplay: Boolean = true, - ) { - fun colorMode(mode: ColorMode) = apply { this.colorMode = mode } - - fun cacheConfig(cacheConfig: CacheConfig) = apply { this.cacheConfig = cacheConfig } - - fun edgeToEdgeDisplay(edgeToEdgeDisplay: Boolean) = apply { this.edgeToEdgeDisplay = edgeToEdgeDisplay } - - fun build(): RoktConfig = RoktConfig(colorMode, cacheConfig, edgeToEdgeDisplay) - } - - enum class ColorMode { LIGHT, DARK, SYSTEM } -} - -class CacheConfig(val cacheDurationInSeconds: Long = DEFAULT_CACHE_DURATION_SECS, val cacheAttributes: Map? = null) { - companion object { - const val DEFAULT_CACHE_DURATION_SECS: Long = 90 * 60 - } -} diff --git a/android-core/src/test/kotlin/com/mparticle/external/ApiVisibilityTest.kt b/android-core/src/test/kotlin/com/mparticle/external/ApiVisibilityTest.kt index 3d2019a31..d796e4de2 100644 --- a/android-core/src/test/kotlin/com/mparticle/external/ApiVisibilityTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/external/ApiVisibilityTest.kt @@ -17,7 +17,7 @@ class ApiVisibilityTest { publicMethodCount++ } } - Assert.assertEquals(63, publicMethodCount) + Assert.assertEquals(62, publicMethodCount) } @Test diff --git a/android-core/src/test/kotlin/com/mparticle/internal/KitFrameworkWrapperTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/KitFrameworkWrapperTest.kt index f1b327c9d..1804a3eb8 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/KitFrameworkWrapperTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/KitFrameworkWrapperTest.kt @@ -29,7 +29,6 @@ import org.powermock.modules.junit4.PowerMockRunner import java.lang.ref.WeakReference import java.util.Random import kotlin.test.assertEquals -import kotlin.test.assertNull @RunWith(PowerMockRunner::class) class KitFrameworkWrapperTest { @@ -721,49 +720,4 @@ class KitFrameworkWrapperTest { verify(mockKitManager).setWrapperSdkVersion(expectedSdkVersion) } - - @Test - fun testGetRoktKitApi_kitManagerNull_returnsNull() { - val wrapper = - KitFrameworkWrapper( - mock( - Context::class.java, - ), - mock(ReportingManager::class.java), - mock(ConfigManager::class.java), - mock(AppStateManager::class.java), - true, - mock(MParticleOptions::class.java), - ) - - val result = wrapper.roktKitApi - - assertNull(result) - } - - @Test - fun testGetRoktKitApi_kitManagerSet_delegatesToKitManager() { - val wrapper = - KitFrameworkWrapper( - mock( - Context::class.java, - ), - mock(ReportingManager::class.java), - mock(ConfigManager::class.java), - mock(AppStateManager::class.java), - true, - mock(MParticleOptions::class.java), - ) - - val mockKitManager = mock(KitManager::class.java) - val mockRoktKitApi = mock(RoktKitApi::class.java) - - `when`(mockKitManager.roktKitApi).thenReturn(mockRoktKitApi) - wrapper.setKitManager(mockKitManager) - - val result = wrapper.roktKitApi - - verify(mockKitManager).roktKitApi - assertEquals(mockRoktKitApi, result) - } } diff --git a/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java b/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java index c7b35f924..14aa17d66 100644 --- a/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java +++ b/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Typeface; import android.location.Location; import android.net.Uri; import android.os.Bundle; @@ -15,15 +14,10 @@ import com.mparticle.BaseEvent; import com.mparticle.MPEvent; import com.mparticle.MParticle; -import com.mparticle.MpRoktEventCallback; -import com.mparticle.RoktEvent; import com.mparticle.WrapperSdkVersion; import com.mparticle.commerce.CommerceEvent; import com.mparticle.consent.ConsentState; import com.mparticle.identity.MParticleUser; -import com.mparticle.rokt.PlacementOptions; -import com.mparticle.rokt.RoktConfig; -import com.mparticle.rokt.RoktEmbeddedView; import org.json.JSONObject; @@ -33,8 +27,6 @@ import java.util.List; import java.util.Map; -import kotlinx.coroutines.flow.Flow; - /** * Base Kit implementation - all Kits must subclass this. */ @@ -590,28 +582,10 @@ public interface BatchListener { /** * Interface for Rokt Kit implementations. * - *

This interface is internal to kit-base and is bridged to the - * {@link com.mparticle.internal.RoktKitApi} interface via a wrapper implementation - * in {@link KitManagerImpl}. The wrapper handles user resolution and - * attribute preparation before delegating to the kit's methods.

- * - * @see com.mparticle.internal.RoktKitApi + *

This interface is internal to kit-base and currently used only for wrapper SDK + * version propagation.

*/ public interface RoktListener { - - void selectPlacements(@NonNull String viewName, - @NonNull Map attributes, - @Nullable MpRoktEventCallback mpRoktEventCallback, - @Nullable Map> placeHolders, - @Nullable Map> fontTypefaces, - @Nullable FilteredMParticleUser user, - @Nullable RoktConfig config, - @Nullable PlacementOptions options); - - Flow events(@NonNull String identifier); - - void enrichAttributes( - @NonNull Map attributes, @Nullable FilteredMParticleUser user); /** * Set the SDK version of the mParticle SDK. * This should match the value set in MParticle.getWrapperSdkVersion() @@ -619,26 +593,5 @@ void enrichAttributes( * @param wrapperSdkVersion the version of the mParticle SDK */ void setWrapperSdkVersion(@NonNull WrapperSdkVersion wrapperSdkVersion); - - void purchaseFinalized(@NonNull String placementId, @NonNull String catalogItemId, boolean status); - - void close(); - - /** - * Set the session id to use for the next execute call. - * This is useful for cases where you have a session id from a non-native integration, - * e.g. WebView, and you want the session to be consistent across integrations. - * - * @param sessionId The session id to be set. Must be a non-empty string. - */ - void setSessionId(@NonNull String sessionId); - - /** - * Get the session id to use within a non-native integration e.g. WebView. - * - * @return The session id or null if no session is present. - */ - @Nullable - String getSessionId(); } } diff --git a/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java b/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java index 1eba0cfac..4d1400c03 100644 --- a/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java +++ b/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java @@ -26,7 +26,6 @@ import com.mparticle.MPEvent; import com.mparticle.MParticle; import com.mparticle.MParticleOptions; -import com.mparticle.internal.RoktKitApi; import com.mparticle.WrapperSdkVersion; import com.mparticle.commerce.CommerceEvent; import com.mparticle.consent.ConsentState; @@ -157,6 +156,11 @@ public int getUserBucket() { return mCoreCallbacks.getUserBucket(); } + @Override + public boolean isEnabled() { + return mCoreCallbacks.isEnabled(); + } + public boolean isOptedOut() { return !mCoreCallbacks.isEnabled(); } @@ -1309,17 +1313,6 @@ public void reset() { } } - @Override - @Nullable - public RoktKitApi getRoktKitApi() { - for (KitIntegration provider : activeKits()) { - if (provider instanceof KitIntegration.RoktListener listener) { - return new RoktKitApiImpl(listener, provider); - } - } - return null; - } - @Override public void setWrapperSdkVersion(@NonNull WrapperSdkVersion wrapperSdkVersion) { for (KitIntegration provider : activeKits()) { diff --git a/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt b/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt index 95de0b0c5..7e7091af9 100644 --- a/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt +++ b/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt @@ -1,15 +1,11 @@ package com.mparticle.kits -import android.content.Context -import android.graphics.Typeface import android.os.Looper import android.os.SystemClock import com.mparticle.BaseEvent import com.mparticle.MPEvent import com.mparticle.MParticle import com.mparticle.MParticleOptions -import com.mparticle.MpRoktEventCallback -import com.mparticle.RoktEvent import com.mparticle.WrapperSdk import com.mparticle.WrapperSdkVersion import com.mparticle.commerce.CommerceEvent @@ -19,7 +15,6 @@ import com.mparticle.consent.GDPRConsent import com.mparticle.identity.IdentityApi import com.mparticle.identity.MParticleUser import com.mparticle.internal.CoreCallbacks -import com.mparticle.internal.Logger import com.mparticle.internal.MPUtility import com.mparticle.internal.SideloadedKit import com.mparticle.kits.KitIntegration.ModifyIdentityListener @@ -28,27 +23,18 @@ import com.mparticle.mock.MockContext import com.mparticle.mock.MockKitConfiguration import com.mparticle.mock.MockKitManagerImpl import com.mparticle.mock.MockMParticle -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig -import com.mparticle.rokt.RoktEmbeddedView import com.mparticle.testutils.TestingUtils import junit.framework.TestCase import junit.framework.TestCase.assertEquals -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.test.runTest import org.json.JSONArray import org.json.JSONException import org.json.JSONObject import org.junit.Assert import org.junit.Assert.assertNull -import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito @@ -60,7 +46,6 @@ import org.mockito.Mockito.withSettings import org.powermock.api.mockito.PowerMockito import org.powermock.core.classloader.annotations.PrepareForTest import org.powermock.modules.junit4.PowerMockRunner -import java.lang.ref.WeakReference import java.util.Arrays import java.util.LinkedList import java.util.concurrent.ConcurrentHashMap @@ -910,617 +895,6 @@ class KitManagerImplTest { Assert.assertEquals(2, manager.providers.size) } - @Test - fun testRokt_non_standard_partner_user_attrs() { - val sideloadedKit = mock(MPSideloadedKit::class.java) - val kitId = 6000000 - - val configJSONObj = - JSONObject().apply { - put("id", kitId) - } - val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj) - `when`(sideloadedKit.configuration).thenReturn(mockedKitConfig) - - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [ - {"map": "number", "value": "no"}, - {"map": "customerId", "value": "minorcatid"} - ] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(mockedKitConfig, settingsMap) - - val mockedProvider = MockProvider(mockedKitConfig) - - val options = - MParticleOptions - .builder(MockContext()) - .sideloadedKits(mutableListOf(sideloadedKit) as List) - .build() - val manager: KitManagerImpl = MockKitManagerImpl(options) - val factory = mock(KitIntegrationFactory::class.java) - manager.setKitFactory(factory) - - `when`(factory.isSupported(Mockito.anyInt())).thenReturn(true) - val supportedKit = mutableSetOf(kitId) - `when`(manager.supportedKits).thenReturn(supportedKit) - `when`(sideloadedKit.isDisabled).thenReturn(false) - `when`( - factory.createInstance( - any( - KitManagerImpl::class.java, - ), - any(KitConfiguration::class.java), - ), - ).thenReturn(sideloadedKit) - manager.providers = - ConcurrentHashMap().apply { - put(42, mockedProvider) - } - - val attributes = - hashMapOf( - Pair("test", "Test"), - Pair("lastname", "Test1"), - Pair("number", "(123) 456-9898"), - Pair("customerId", "55555"), - Pair("country", "US"), - ) - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - val finalAttributes = mockedProvider.lastAttributes - Assert.assertNotNull(finalAttributes) - finalAttributes!! - Assert.assertEquals(6, finalAttributes.size) - Assert.assertEquals("(123) 456-9898", finalAttributes["no"]) - Assert.assertEquals("55555", finalAttributes["minorcatid"]) - Assert.assertEquals("Test1", finalAttributes["lastname"]) - Assert.assertEquals("Test", finalAttributes["test"]) - Assert.assertEquals("US", finalAttributes["country"]) - Assert.assertEquals("false", finalAttributes["sandbox"]) - } - - @Test - fun testSelectPlacements_shouldNotModifyAttributes_ifMappedKeysDoNotExist() { - val sideloadedKit = mock(MPSideloadedKit::class.java) - val kitId = 6000000 - - val configJSONObj = - JSONObject().apply { - put("id", kitId) - } - val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj) - `when`(sideloadedKit.configuration).thenReturn(mockedKitConfig) - - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [ - {"map": "number", "value": "no"}, - {"map": "customerId", "value": "minorcatid"} - ] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(mockedKitConfig, settingsMap) - - val mockedProvider = MockProvider(mockedKitConfig) - - val options = - MParticleOptions - .builder(MockContext()) - .sideloadedKits(mutableListOf(sideloadedKit) as List) - .build() - val manager: KitManagerImpl = MockKitManagerImpl(options) - val factory = mock(KitIntegrationFactory::class.java) - manager.setKitFactory(factory) - - `when`(factory.isSupported(Mockito.anyInt())).thenReturn(true) - val supportedKit = mutableSetOf(kitId) - `when`(manager.supportedKits).thenReturn(supportedKit) - `when`(sideloadedKit.isDisabled).thenReturn(false) - `when`( - factory.createInstance( - any( - KitManagerImpl::class.java, - ), - any(KitConfiguration::class.java), - ), - ).thenReturn(sideloadedKit) - manager.providers = - ConcurrentHashMap().apply { - put(42, mockedProvider) - } - - val attributes = - hashMapOf( - Pair("test", "Test"), - Pair("lastname", "Test1"), - Pair("call", "(123) 456-9898"), - Pair("postal", "5-45555"), - Pair("country", "US"), - ) - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - val finalAttributes = mockedProvider.lastAttributes - Assert.assertNotNull(finalAttributes) - finalAttributes!! - Assert.assertEquals(6, finalAttributes.size) - - Assert.assertEquals("(123) 456-9898", finalAttributes["call"]) - Assert.assertEquals("5-45555", finalAttributes["postal"]) - Assert.assertEquals("Test1", finalAttributes["lastname"]) - Assert.assertEquals("Test", finalAttributes["test"]) - Assert.assertEquals("US", finalAttributes["country"]) - Assert.assertEquals("false", finalAttributes["sandbox"]) - } - - @Test - fun testSelectPlacements_shouldNotModifyAttributes_ifMapAndValueKeysAreSame() { - val sideloadedKit = mock(MPSideloadedKit::class.java) - val kitId = 6000000 - - val configJSONObj = - JSONObject().apply { - put("id", kitId) - } - val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj) - `when`(sideloadedKit.configuration).thenReturn(mockedKitConfig) - - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [ - {"map": "number", "value": "no"}, - {"map": "customerId", "value": "minorcatid"} - ] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(mockedKitConfig, settingsMap) - - val mockedProvider = MockProvider(mockedKitConfig) - - val options = - MParticleOptions - .builder(MockContext()) - .sideloadedKits(mutableListOf(sideloadedKit) as List) - .build() - val manager: KitManagerImpl = MockKitManagerImpl(options) - val factory = mock(KitIntegrationFactory::class.java) - manager.setKitFactory(factory) - - `when`(factory.isSupported(Mockito.anyInt())).thenReturn(true) - val supportedKit = mutableSetOf(kitId) - `when`(manager.supportedKits).thenReturn(supportedKit) - `when`(sideloadedKit.isDisabled).thenReturn(false) - `when`( - factory.createInstance( - any( - KitManagerImpl::class.java, - ), - any(KitConfiguration::class.java), - ), - ).thenReturn(sideloadedKit) - manager.providers = - ConcurrentHashMap().apply { - put(42, mockedProvider) - } - - val attributes = - hashMapOf( - Pair("test", "Test"), - Pair("lastname", "Test1"), - Pair("no", "(123) 456-9898"), - Pair("minorcatid", "5-45555"), - Pair("country", "US"), - ) - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - val finalAttributes = mockedProvider.lastAttributes - Assert.assertNotNull(finalAttributes) - finalAttributes!! - Assert.assertEquals(6, finalAttributes.size) - Assert.assertEquals("(123) 456-9898", finalAttributes["no"]) - Assert.assertEquals("5-45555", finalAttributes["minorcatid"]) - Assert.assertEquals("Test1", finalAttributes["lastname"]) - Assert.assertEquals("Test", finalAttributes["test"]) - Assert.assertEquals("US", finalAttributes["country"]) - Assert.assertEquals("false", finalAttributes["sandbox"]) - } - - @Test - fun testRokt_non_standard_partner_user_attrs_When_placementAttributes_is_empty() { - val sideloadedKit = mock(MPSideloadedKit::class.java) - val kitId = 6000000 - - val configJSONObj = - JSONObject().apply { - put("id", kitId) - } - val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj) - `when`(sideloadedKit.configuration).thenReturn(mockedKitConfig) - - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [ - ] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(mockedKitConfig, settingsMap) - - val mockedProvider = MockProvider(mockedKitConfig) - - val options = - MParticleOptions - .builder(MockContext()) - .sideloadedKits(mutableListOf(sideloadedKit) as List) - .build() - val manager: KitManagerImpl = MockKitManagerImpl(options) - val factory = mock(KitIntegrationFactory::class.java) - manager.setKitFactory(factory) - - `when`(factory.isSupported(Mockito.anyInt())).thenReturn(true) - val supportedKit = mutableSetOf(kitId) - `when`(manager.supportedKits).thenReturn(supportedKit) - `when`(sideloadedKit.isDisabled).thenReturn(false) - `when`( - factory.createInstance( - any( - KitManagerImpl::class.java, - ), - any(KitConfiguration::class.java), - ), - ).thenReturn(sideloadedKit) - manager.providers = - ConcurrentHashMap().apply { - put(42, mockedProvider) - } - - val attributes = - hashMapOf( - Pair("test", "Test"), - Pair("lastname", "Test1"), - Pair("number", "(123) 456-9898"), - Pair("customerId", "55555"), - Pair("country", "US"), - ) - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - val finalAttributes = mockedProvider.lastAttributes - Assert.assertNotNull(finalAttributes) - finalAttributes!! - Assert.assertEquals(6, finalAttributes.size) - Assert.assertEquals("(123) 456-9898", finalAttributes["number"]) - Assert.assertEquals("55555", finalAttributes["customerId"]) - Assert.assertEquals("Test1", finalAttributes["lastname"]) - Assert.assertEquals("Test", finalAttributes["test"]) - Assert.assertEquals("US", finalAttributes["country"]) - Assert.assertEquals("false", finalAttributes["sandbox"]) - } - - @Test - fun testRokt_SandboxMode_When_Default_Environment() { - val sideloadedKit = mock(MPSideloadedKit::class.java) - val kitId = 6000000 - - val configJSONObj = - JSONObject().apply { - put("id", kitId) - } - val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj) - `when`(sideloadedKit.configuration).thenReturn(mockedKitConfig) - - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(mockedKitConfig, settingsMap) - - val mockedProvider = MockProvider(mockedKitConfig) - - val options = - MParticleOptions - .builder(MockContext()) - .sideloadedKits(mutableListOf(sideloadedKit) as List) - .build() - val manager: KitManagerImpl = MockKitManagerImpl(options) - val factory = mock(KitIntegrationFactory::class.java) - manager.setKitFactory(factory) - - `when`(factory.isSupported(Mockito.anyInt())).thenReturn(true) - val supportedKit = mutableSetOf(kitId) - `when`(manager.supportedKits).thenReturn(supportedKit) - `when`(sideloadedKit.isDisabled).thenReturn(false) - `when`( - factory.createInstance( - any( - KitManagerImpl::class.java, - ), - any(KitConfiguration::class.java), - ), - ).thenReturn(sideloadedKit) - manager.providers = - ConcurrentHashMap().apply { - put(42, mockedProvider) - } - - val attributes = - hashMapOf( - Pair("test", "Test"), - Pair("lastname", "Test1"), - Pair("number", "(123) 456-9898"), - Pair("customerId", "55555"), - Pair("country", "US"), - ) - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - val finalAttributes = mockedProvider.lastAttributes - Assert.assertNotNull(finalAttributes) - finalAttributes!! - Assert.assertEquals(6, finalAttributes.size) - Assert.assertEquals("(123) 456-9898", finalAttributes["number"]) - Assert.assertEquals("55555", finalAttributes["customerId"]) - Assert.assertEquals("Test1", finalAttributes["lastname"]) - Assert.assertEquals("Test", finalAttributes["test"]) - Assert.assertEquals("US", finalAttributes["country"]) - Assert.assertEquals("false", finalAttributes["sandbox"]) - } - - @Test - fun testRokt_selectPlacements_with_PlacementOptions() { - val mockUser = mock(MParticleUser::class.java) - `when`(mockIdentity!!.currentUser).thenReturn(mockUser) - - val manager: KitManagerImpl = MockKitManagerImpl() - val roktListener = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(roktListener.isDisabled).thenReturn(false) - manager.providers = - ConcurrentHashMap().apply { - put(1, roktListener) - } - - val attributes = hashMapOf() - val placementOptions = PlacementOptions(jointSdkSelectPlacements = 123L) - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null, placementOptions) - - val optionsCaptor = ArgumentCaptor.forClass(PlacementOptions::class.java) - verify(roktListener as KitIntegration.RoktListener).selectPlacements( - any(), - any(), - any(), - any(), - any(), - any(), - any(), - optionsCaptor.capture(), - ) - assertSame(placementOptions, optionsCaptor.value) - } - - @Test - fun testRokt_selectPlacements_without_PlacementOptions() { - val mockUser = mock(MParticleUser::class.java) - `when`(mockIdentity!!.currentUser).thenReturn(mockUser) - - val manager: KitManagerImpl = MockKitManagerImpl() - val roktListener = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(roktListener.isDisabled).thenReturn(false) - manager.providers = - ConcurrentHashMap().apply { - put(1, roktListener) - } - - val attributes = hashMapOf() - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - - val optionsCaptor = ArgumentCaptor.forClass(PlacementOptions::class.java) - verify(roktListener as KitIntegration.RoktListener).selectPlacements( - any(), - any(), - any(), - any(), - any(), - any(), - any(), - optionsCaptor.capture(), - ) - assertNull(optionsCaptor.value) - } - - @Test - fun testRokt_SandboxMode_When_Environment_IS_Development() { - val sideloadedKit = mock(MPSideloadedKit::class.java) - val kitId = 6000000 - - val configJSONObj = - JSONObject().apply { - put("id", kitId) - } - val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj) - - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(mockedKitConfig, settingsMap) - - val mockedProvider = MockProvider(mockedKitConfig) - - val options = - MParticleOptions - .builder(MockContext()) - .sideloadedKits(mutableListOf(sideloadedKit) as List) - .build() - val manager: KitManagerImpl = MockKitManagerImpl(options) - val factory = mock(KitIntegrationFactory::class.java) - manager.setKitFactory(factory) - - `when`(factory.isSupported(Mockito.anyInt())).thenReturn(true) - PowerMockito.mockStatic(MPUtility::class.java) - `when`(MPUtility.isDevEnv()).thenReturn(true) - val supportedKit = mutableSetOf(kitId) - `when`(manager.supportedKits).thenReturn(supportedKit) - `when`(sideloadedKit.isDisabled).thenReturn(false) - `when`( - factory.createInstance( - any( - KitManagerImpl::class.java, - ), - any(KitConfiguration::class.java), - ), - ).thenReturn(sideloadedKit) - manager.providers = - ConcurrentHashMap().apply { - put(42, mockedProvider) - } - - val attributes = - hashMapOf( - Pair("test", "Test"), - Pair("lastname", "Test1"), - Pair("number", "(123) 456-9898"), - Pair("customerId", "55555"), - Pair("country", "US"), - ) - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - val finalAttributes = mockedProvider.lastAttributes - Assert.assertNotNull(finalAttributes) - finalAttributes!! - Assert.assertEquals(6, finalAttributes.size) - Assert.assertEquals("(123) 456-9898", finalAttributes["number"]) - Assert.assertEquals("55555", finalAttributes["customerId"]) - Assert.assertEquals("Test1", finalAttributes["lastname"]) - Assert.assertEquals("Test", finalAttributes["test"]) - Assert.assertEquals("US", finalAttributes["country"]) - Assert.assertEquals("true", finalAttributes["sandbox"]) - } - - @Test - fun testRokt_SandboxMode_When_SandBox_is_Pass_In_Attributes_And_Environment_Is_DEV() { - val sideloadedKit = mock(MPSideloadedKit::class.java) - val kitId = 6000000 - - val configJSONObj = - JSONObject().apply { - put("id", kitId) - } - val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj) - - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(mockedKitConfig, settingsMap) - - val mockedProvider = MockProvider(mockedKitConfig) - - val options = - MParticleOptions - .builder(MockContext()) - .sideloadedKits(mutableListOf(sideloadedKit) as List) - .build() - val manager: KitManagerImpl = MockKitManagerImpl(options) - val factory = mock(KitIntegrationFactory::class.java) - manager.setKitFactory(factory) - - `when`(factory.isSupported(Mockito.anyInt())).thenReturn(true) - PowerMockito.mockStatic(MPUtility::class.java) - `when`(MPUtility.isDevEnv()).thenReturn(true) - val supportedKit = mutableSetOf(kitId) - `when`(manager.supportedKits).thenReturn(supportedKit) - `when`(sideloadedKit.isDisabled).thenReturn(false) - `when`( - factory.createInstance( - any( - KitManagerImpl::class.java, - ), - any(KitConfiguration::class.java), - ), - ).thenReturn(sideloadedKit) - manager.providers = - ConcurrentHashMap().apply { - put(42, mockedProvider) - } - - val attributes = - hashMapOf( - Pair("test", "Test"), - Pair("lastname", "Test1"), - Pair("number", "(123) 456-9898"), - Pair("customerId", "55555"), - Pair("country", "US"), - Pair("sandbox", "false"), - ) - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - roktApi!!.selectPlacements("Test", attributes, null, null, null, null) - val finalAttributes = mockedProvider.lastAttributes - Assert.assertNotNull(finalAttributes) - finalAttributes!! - Assert.assertEquals(6, finalAttributes.size) - Assert.assertEquals("(123) 456-9898", finalAttributes["number"]) - Assert.assertEquals("55555", finalAttributes["customerId"]) - Assert.assertEquals("Test1", finalAttributes["lastname"]) - Assert.assertEquals("Test", finalAttributes["test"]) - Assert.assertEquals("US", finalAttributes["country"]) - Assert.assertEquals("false", finalAttributes["sandbox"]) - } - @Test fun testSetWrapperSdkVersion() { val manager: KitManagerImpl = MockKitManagerImpl() @@ -1558,308 +932,6 @@ class KitManagerImplTest { .setWrapperSdkVersion(wrapperSdkVersion) } - @Test - fun testEvents_noProviders_returnsEmptyFlow() { - val manager: KitManagerImpl = MockKitManagerImpl() - - val roktApi = manager.getRoktKitApi() - assertNull(roktApi) - val result = roktApi?.events("test-identifier") ?: flowOf() - - runTest { - val elements = result.toList() - assertTrue(elements.isEmpty()) - } - } - - @Test - fun testEvents_providersExistButNotRoktListeners_returnsEmptyFlow() { - val manager: KitManagerImpl = MockKitManagerImpl() - - val nonRoktProvider = mock(KitIntegration::class.java) - `when`(nonRoktProvider.isDisabled).thenReturn(false) - `when`(nonRoktProvider.getName()).thenReturn("NonRoktProvider") - - manager.providers = - ConcurrentHashMap().apply { - put(1, nonRoktProvider) - } - - val roktApi = manager.getRoktKitApi() - assertNull(roktApi) - val result = roktApi?.events("test-identifier") ?: flowOf() - - runTest { - val elements = result.toList() - assertTrue(elements.isEmpty()) - } - } - - @Test - fun testEvents_roktListenerDisabled_returnsEmptyFlow() { - val manager: KitManagerImpl = MockKitManagerImpl() - - val disabledRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(disabledRoktProvider.isDisabled).thenReturn(true) - `when`(disabledRoktProvider.getName()).thenReturn("DisabledRoktProvider") - - manager.providers = - ConcurrentHashMap().apply { - put(1, disabledRoktProvider) - } - - val roktApi = manager.getRoktKitApi() - assertNull(roktApi) - val result = roktApi?.events("test-identifier") ?: flowOf() - - runTest { - val elements = result.toList() - assertTrue(elements.isEmpty()) - } - } - - @Test - fun testEvents_roktListenerEnabled_delegatesToProvider() { - val manager: KitManagerImpl = MockKitManagerImpl() - - val enabledRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(enabledRoktProvider.isDisabled).thenReturn(false) - `when`(enabledRoktProvider.getName()).thenReturn("EnabledRoktProvider") - - val expectedFlow: Flow = flowOf() - val testIdentifier = "test-identifier" - `when`((enabledRoktProvider as KitIntegration.RoktListener).events(testIdentifier)) - .thenReturn(expectedFlow) - - manager.providers = - ConcurrentHashMap().apply { - put(1, enabledRoktProvider) - } - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - val result = roktApi!!.events(testIdentifier) - - verify(enabledRoktProvider as KitIntegration.RoktListener).events(testIdentifier) - assertEquals(expectedFlow, result) - } - - @Test - fun testEvents_multipleProviders_usesFirstEnabledRoktListener() { - val manager: KitManagerImpl = MockKitManagerImpl() - - val nonRoktProvider = mock(KitIntegration::class.java) - `when`(nonRoktProvider.isDisabled).thenReturn(false) - `when`(nonRoktProvider.getName()).thenReturn("NonRoktProvider") - - val disabledRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(disabledRoktProvider.isDisabled).thenReturn(true) - `when`(disabledRoktProvider.getName()).thenReturn("DisabledRoktProvider") - - val enabledRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(enabledRoktProvider.isDisabled).thenReturn(false) - `when`(enabledRoktProvider.getName()).thenReturn("EnabledRoktProvider") - - val secondEnabledRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(secondEnabledRoktProvider.isDisabled).thenReturn(false) - `when`(secondEnabledRoktProvider.getName()).thenReturn("SecondEnabledRoktProvider") - - val expectedFlow: Flow = flowOf() - val testIdentifier = "test-identifier" - `when`((enabledRoktProvider as KitIntegration.RoktListener).events(testIdentifier)) - .thenReturn(expectedFlow) - `when`((secondEnabledRoktProvider as KitIntegration.RoktListener).events(testIdentifier)) - .thenReturn(flowOf()) - - manager.providers = - ConcurrentHashMap().apply { - put(1, nonRoktProvider) - put(2, disabledRoktProvider) - put(3, enabledRoktProvider) - put(4, secondEnabledRoktProvider) - } - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - val result = roktApi!!.events(testIdentifier) - - verify(enabledRoktProvider as KitIntegration.RoktListener).events(testIdentifier) - verify(secondEnabledRoktProvider as KitIntegration.RoktListener, never()).events(any()) - assertEquals(expectedFlow, result) - } - - @Test - fun testEvents_providerThrowsException_returnsEmptyFlow() { - val manager: KitManagerImpl = MockKitManagerImpl() - - val exceptionRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(exceptionRoktProvider.isDisabled).thenReturn(false) - `when`(exceptionRoktProvider.getName()).thenReturn("ExceptionRoktProvider") - `when`((exceptionRoktProvider as KitIntegration.RoktListener).events(any())) - .thenThrow(RuntimeException("Test exception")) - - manager.providers = - ConcurrentHashMap().apply { - put(1, exceptionRoktProvider) - } - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - val result = roktApi!!.events("test-identifier") - - runTest { - val elements = result.toList() - assertTrue(elements.isEmpty()) - } - } - - @Test - fun testEvents_providerThrowsException_returnsEmptyFlowWithoutFallback() { - val manager: KitManagerImpl = MockKitManagerImpl() - - val exceptionRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(exceptionRoktProvider.isDisabled).thenReturn(false) - `when`(exceptionRoktProvider.getName()).thenReturn("ExceptionRoktProvider") - `when`((exceptionRoktProvider as KitIntegration.RoktListener).events(any())) - .thenThrow(RuntimeException("Test exception")) - - val workingRoktProvider = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(workingRoktProvider.isDisabled).thenReturn(false) - `when`(workingRoktProvider.getName()).thenReturn("WorkingRoktProvider") - - val expectedFlow: Flow = flowOf() - val testIdentifier = "test-identifier" - `when`((workingRoktProvider as KitIntegration.RoktListener).events(testIdentifier)) - .thenReturn(expectedFlow) - - manager.providers = - ConcurrentHashMap().apply { - put(1, exceptionRoktProvider) - put(2, workingRoktProvider) - } - - val roktApi = manager.getRoktKitApi() - Assert.assertNotNull(roktApi) - val result = roktApi!!.events(testIdentifier) - - verify(workingRoktProvider as KitIntegration.RoktListener, never()).events(any()) - runTest { - val elements = result.toList() - assertTrue(elements.isEmpty()) - } - } - - internal inner class MockProvider( - val config: KitConfiguration, - ) : KitIntegration(), - KitIntegration.RoktListener { - var lastAttributes: Map? = null - var lastOptions: PlacementOptions? = null - var lastUser: FilteredMParticleUser? = null - - override fun isDisabled(): Boolean = false - - override fun getName(): String = "FakeProvider" - - override fun onKitCreate( - settings: MutableMap?, - context: Context?, - ): MutableList { - TODO("Not yet implemented") - } - - override fun setOptOut(optedOut: Boolean): MutableList { - TODO("Not yet implemented") - } - - override fun getConfiguration(): KitConfiguration = config - - override fun selectPlacements( - viewName: String, - attributes: MutableMap, - mpRoktEventCallback: MpRoktEventCallback?, - placeHolders: MutableMap>?, - fontTypefaces: MutableMap>?, - user: FilteredMParticleUser?, - config: RoktConfig?, - options: PlacementOptions?, - ) { - lastAttributes = attributes.toMap() - lastOptions = options - lastUser = user - Logger.info("selectPlacements with $attributes and options $options") - } - - override fun events(identifier: String): Flow { - Logger.info("events called with identfier: $identifier") - return flowOf() - } - - override fun enrichAttributes( - attributes: MutableMap, - user: FilteredMParticleUser?, - ) { - Logger.info("callRoktComposable with $attributes") - } - - override fun setWrapperSdkVersion(wrapperSdkVersion: WrapperSdkVersion) { - Logger.info("setWrapperSdkVersion with $wrapperSdkVersion") - } - - override fun purchaseFinalized( - placementId: String, - catalogItemId: String, - status: Boolean, - ) { - Logger.info("purchaseFinalized with placementId: $placementId catalogItemId : $catalogItemId status : $status ") - } - - override fun close() { - Logger.info("close called") - } - - override fun setSessionId(sessionId: String) { - Logger.info("setSessionId called with $sessionId") - } - - override fun getSessionId(): String? { - Logger.info("getSessionId called") - return null - } - } - internal inner class KitManagerEventCounter : MockKitManagerImpl() { var logBaseEventCalled = 0 var logCommerceEventCalled = 0 diff --git a/android-kit-base/src/test/kotlin/com/mparticle/kits/RoktKitApiImplTest.kt b/android-kit-base/src/test/kotlin/com/mparticle/kits/RoktKitApiImplTest.kt deleted file mode 100644 index 65643eba5..000000000 --- a/android-kit-base/src/test/kotlin/com/mparticle/kits/RoktKitApiImplTest.kt +++ /dev/null @@ -1,167 +0,0 @@ -package com.mparticle.kits - -import com.mparticle.MParticle -import com.mparticle.identity.IdentityApi -import com.mparticle.internal.MPUtility -import com.mparticle.mock.MockMParticle -import com.mparticle.rokt.PlacementOptions -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.test.runTest -import org.json.JSONObject -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.any -import org.mockito.Mockito.mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.Mockito.withSettings - -class RoktKitApiImplTest { - @Before - fun setUp() { - val identityApi = mock(IdentityApi::class.java) - val instance = MockMParticle() - instance.setIdentityApi(identityApi) - MParticle.setInstance(instance) - } - - @Test - fun testSelectPlacements_mapsAttributesAndAddsSandbox() { - val kitConfig = KitConfiguration.createKitConfiguration(JSONObject().put("id", 42)) - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [ - {"map": "number", "value": "no"}, - {"map": "customerId", "value": "minorcatid"} - ] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(kitConfig, settingsMap) - - val kitIntegration = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(kitIntegration.configuration).thenReturn(kitConfig) - val roktListener = kitIntegration as KitIntegration.RoktListener - val roktApi = RoktKitApiImpl(roktListener, kitIntegration) - - val attributes = - hashMapOf( - "number" to "(123) 456-9898", - "customerId" to "55555", - "country" to "US", - ) - - roktApi.selectPlacements("Test", attributes, null, null, null, null, null) - - @Suppress("UNCHECKED_CAST") - val attributesCaptor = ArgumentCaptor.forClass(Map::class.java) as ArgumentCaptor> - verify(roktListener).selectPlacements( - any(), - attributesCaptor.capture(), - any(), - any(), - any(), - any(), - any(), - any(), - ) - val captured = attributesCaptor.value - assertEquals("(123) 456-9898", captured["no"]) - assertEquals("55555", captured["minorcatid"]) - assertEquals("US", captured["country"]) - assertEquals(MPUtility.isDevEnv().toString(), captured["sandbox"]) - } - - @Test - fun testSelectPlacements_passesPlacementOptions() { - val kitConfig = KitConfiguration.createKitConfiguration(JSONObject().put("id", 42)) - val kitIntegration = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(kitIntegration.configuration).thenReturn(kitConfig) - val roktListener = kitIntegration as KitIntegration.RoktListener - val roktApi = RoktKitApiImpl(roktListener, kitIntegration) - - val placementOptions = PlacementOptions(jointSdkSelectPlacements = 123L) - - roktApi.selectPlacements("Test", emptyMap(), null, null, null, null, placementOptions) - - val optionsCaptor = ArgumentCaptor.forClass(PlacementOptions::class.java) - verify(roktListener).selectPlacements( - any(), - any(), - any(), - any(), - any(), - any(), - any(), - optionsCaptor.capture(), - ) - assertEquals(placementOptions, optionsCaptor.value) - } - - @Test - fun testEvents_returnsEmptyFlowWhenProviderThrows() = runTest { - val kitConfig = KitConfiguration.createKitConfiguration(JSONObject().put("id", 42)) - val kitIntegration = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(kitIntegration.configuration).thenReturn(kitConfig) - val roktListener = kitIntegration as KitIntegration.RoktListener - `when`(roktListener.events(any())).thenThrow(RuntimeException("Test exception")) - val roktApi = RoktKitApiImpl(roktListener, kitIntegration) - - val result = roktApi.events("test-identifier") - - assertTrue(result.toList().isEmpty()) - } - - @Test - fun testPrepareAttributesAsync_delegatesToEnrichAttributes() { - val kitConfig = KitConfiguration.createKitConfiguration(JSONObject().put("id", 42)) - val settingsMap = - hashMapOf( - "placementAttributesMapping" to - """ - [ - {"map": "number", "value": "no"} - ] - """.trimIndent(), - ) - val field = KitConfiguration::class.java.getDeclaredField("settings") - field.isAccessible = true - field.set(kitConfig, settingsMap) - - val kitIntegration = - mock( - KitIntegration::class.java, - withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), - ) - `when`(kitIntegration.configuration).thenReturn(kitConfig) - val roktListener = kitIntegration as KitIntegration.RoktListener - val roktApi = RoktKitApiImpl(roktListener, kitIntegration) - - roktApi.prepareAttributesAsync(mapOf("number" to "(123) 456-9898")) - - @Suppress("UNCHECKED_CAST") - val attributesCaptor = ArgumentCaptor.forClass(Map::class.java) as ArgumentCaptor> - verify(roktListener).enrichAttributes(attributesCaptor.capture(), any()) - val captured = attributesCaptor.value - assertEquals("(123) 456-9898", captured["no"]) - assertEquals(MPUtility.isDevEnv().toString(), captured["sandbox"]) - } -} diff --git a/buildSrc/src/main/kotlin/com/mparticle/MavenCentralPublish.kt b/buildSrc/src/main/kotlin/com/mparticle/MavenCentralPublish.kt index e593c6466..840486690 100644 --- a/buildSrc/src/main/kotlin/com/mparticle/MavenCentralPublish.kt +++ b/buildSrc/src/main/kotlin/com/mparticle/MavenCentralPublish.kt @@ -77,14 +77,15 @@ fun Project.configureMavenPublishing(mparticleMavenPublish: MParticleMavenPublis } } + val capitalizedPublicationName = publicationName.capitalizeForTaskName() val validateTaskName = - "validatePomFor${publicationName.replaceFirstChar { it.uppercaseChar() }}Publication" + "validatePomFor${capitalizedPublicationName}Publication" tasks.register(validateTaskName, ValidatePomTask::class.java) { description = "Validates the generated POM file for the '$publicationName' publication." group = "verification" pomFile.set(project.layout.buildDirectory.file("publications/$publicationName/pom-default.xml")) - dependsOn("generatePomFileFor${publicationName.replaceFirstChar { it.uppercaseChar() }}Publication") + dependsOn("generatePomFileFor${capitalizedPublicationName}Publication") } tasks.withType(PublishToMavenLocal::class.java).configureEach { @@ -94,3 +95,9 @@ fun Project.configureMavenPublishing(mparticleMavenPublish: MParticleMavenPublis } } } + +private fun String.capitalizeForTaskName(): String = if (isEmpty()) { + this +} else { + substring(0, 1).uppercase() + substring(1) +} diff --git a/kits/rokt/rokt/build.gradle b/kits/rokt/rokt/build.gradle index 4d49a2422..bf6fa5a84 100644 --- a/kits/rokt/rokt/build.gradle +++ b/kits/rokt/rokt/build.gradle @@ -80,6 +80,11 @@ dependencies { testImplementation files('libs/java-json.jar') testImplementation 'com.squareup.assertj:assertj-android:1.2.0' testImplementation ("io.mockk:mockk:1.13.4") + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.powermock:powermock-module-junit4:2.0.7' + testImplementation 'org.powermock:powermock-api-mockito2:2.0.2' + testImplementation 'org.powermock:powermock-core:2.0.7' + testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' compileOnly 'androidx.compose.ui:ui' compileOnly 'androidx.compose.material:material' diff --git a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/MParticleRokt.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/MParticleRokt.kt new file mode 100644 index 000000000..12a079a93 --- /dev/null +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/MParticleRokt.kt @@ -0,0 +1,32 @@ +package com.mparticle.kits + +import com.mparticle.MParticle + +/** + * Java-friendly accessors for the legacy Rokt API object. + */ +object MParticleRokt { + @Volatile + private var rokt: Rokt? = null + + @Suppress("FunctionName") + @JvmStatic + fun Rokt(): Rokt { + val mParticle = requireNotNull(MParticle.getInstance()) { + "MParticle must be started before calling MParticleRokt.Rokt()" + } + + synchronized(this) { + rokt?.let { return it } + + return createRokt(mParticle).also { + rokt = it + } + } + } +} + +private fun createRokt(mParticle: MParticle): Rokt { + val kitManager = mParticle.Internal().kitManager + return Rokt(kitManager) +} diff --git a/android-core/src/main/kotlin/com/mparticle/Rokt.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/Rokt.kt similarity index 51% rename from android-core/src/main/kotlin/com/mparticle/Rokt.kt rename to kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/Rokt.kt index e531b7f44..0a186ae82 100644 --- a/android-core/src/main/kotlin/com/mparticle/Rokt.kt +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/Rokt.kt @@ -1,20 +1,21 @@ -package com.mparticle +package com.mparticle.kits import android.graphics.Typeface -import com.mparticle.internal.ConfigManager +import com.mparticle.MParticle import com.mparticle.internal.KitManager import com.mparticle.internal.Logger -import com.mparticle.internal.listeners.ApiClass -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig -import com.mparticle.rokt.RoktEmbeddedView +import com.rokt.roktsdk.PlacementOptions +import com.rokt.roktsdk.Rokt.RoktCallback +import com.rokt.roktsdk.RoktConfig +import com.rokt.roktsdk.RoktEvent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import java.lang.ref.WeakReference -@ApiClass -class Rokt internal constructor(private val mConfigManager: ConfigManager, private val mKitManager: KitManager) { - +/** + * Public facade for interacting with the Rokt Kit through mParticle. + */ +class Rokt internal constructor(private val mKitManager: KitManager) { /** * Display a Rokt placement with the specified parameters. * @@ -29,15 +30,26 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva fun selectPlacements( identifier: String, attributes: Map, - callbacks: MpRoktEventCallback? = null, + callbacks: RoktCallback? = null, embeddedViews: Map>? = null, fontTypefaces: Map>? = null, config: RoktConfig? = null, ) { - if (mConfigManager.isEnabled) { - val roktApi = mKitManager.roktKitApi - if (roktApi != null) { - roktApi.selectPlacements(identifier, HashMap(attributes), callbacks, embeddedViews, fontTypefaces, config, buildPlacementOptions()) + if (isEnabled()) { + val resolved = resolveRoktKit() + if (resolved != null) { + val (kitIntegration, roktListener) = resolved + RoktKitRequestHelper.selectPlacements( + kitIntegration = kitIntegration, + roktListener = roktListener, + viewName = identifier, + attributes = HashMap(attributes), + roktCallback = callbacks, + placeHolders = embeddedViews, + fontTypefaces = fontTypefaces, + config = config, + options = buildPlacementOptions(), + ) } else { Logger.warning("Rokt Kit is not available. Make sure the Rokt Kit is included in your app.") } @@ -50,8 +62,8 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva * @param identifier The placement identifier to listen for events * @return A Flow emitting RoktEvent objects */ - fun events(identifier: String): Flow = if (mConfigManager.isEnabled) { - mKitManager.roktKitApi?.events(identifier) ?: flowOf() + fun events(identifier: String): Flow = if (isEnabled()) { + resolveRoktKit()?.second?.events(identifier) ?: flowOf() } else { flowOf() } @@ -64,8 +76,8 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva * @param status Whether the purchase was successful */ fun purchaseFinalized(placementId: String, catalogItemId: String, status: Boolean) { - if (mConfigManager.isEnabled) { - mKitManager.roktKitApi?.purchaseFinalized(placementId, catalogItemId, status) + if (isEnabled()) { + resolveRoktKit()?.second?.purchaseFinalized(placementId, catalogItemId, status) } } @@ -73,8 +85,8 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva * Close any active Rokt placements. */ fun close() { - if (mConfigManager.isEnabled) { - mKitManager.roktKitApi?.close() + if (isEnabled()) { + resolveRoktKit()?.second?.close() } } @@ -89,8 +101,8 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva * @param sessionId The session id to be set. Must be a non-empty string. */ fun setSessionId(sessionId: String) { - if (mConfigManager.isEnabled) { - mKitManager.roktKitApi?.setSessionId(sessionId) + if (isEnabled()) { + resolveRoktKit()?.second?.setSessionId(sessionId) } } @@ -99,8 +111,8 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva * * @return The session id or null if no session is present or SDK is not initialized. */ - fun getSessionId(): String? = if (mConfigManager.isEnabled) { - mKitManager.roktKitApi?.getSessionId() + fun getSessionId(): String? = if (isEnabled()) { + resolveRoktKit()?.second?.getSessionId() } else { null } @@ -110,13 +122,33 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva * * @param attributes The attributes to prepare */ - fun prepareAttributesAsync(attributes: Map) { - if (mConfigManager.isEnabled) { - mKitManager.roktKitApi?.prepareAttributesAsync(attributes) + internal fun prepareAttributesAsync(attributes: Map) { + if (isEnabled()) { + val resolved = resolveRoktKit() + if (resolved != null) { + val (kitIntegration, roktListener) = resolved + RoktKitRequestHelper.prepareAttributesAsync( + kitIntegration = kitIntegration, + roktListener = roktListener, + attributes = attributes, + ) + } + } + } + + private fun resolveRoktKit(): Pair? { + if (!mKitManager.isKitActive(MParticle.ServiceProviders.ROKT)) { + return null } + val kitInstance = mKitManager.getKitInstance(MParticle.ServiceProviders.ROKT) as? KitIntegration ?: return null + val roktBridge = kitInstance as? RoktKitBridge ?: return null + return kitInstance to roktBridge } + private fun isEnabled(): Boolean = mKitManager.isEnabled + private fun buildPlacementOptions(): PlacementOptions = PlacementOptions( jointSdkSelectPlacements = System.currentTimeMillis(), + dynamicPerformanceMarkers = mapOf(), ) } diff --git a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktConfigExtensions.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktConfigExtensions.kt deleted file mode 100644 index 45a6ee4f8..000000000 --- a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktConfigExtensions.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.mparticle.kits - -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig -import com.rokt.roktsdk.CacheConfig -import com.mparticle.rokt.CacheConfig as MpCacheConfig -import com.rokt.roktsdk.RoktConfig as RoktSdkConfig - -fun MpCacheConfig.toRoktSdkCacheConfig(): CacheConfig = CacheConfig( - cacheDurationInSeconds = this.cacheDurationInSeconds, - cacheAttributes = this.cacheAttributes, -) - -fun RoktConfig.toRoktSdkConfig(): RoktSdkConfig { - val colorMode = when (this.colorMode) { - RoktConfig.ColorMode.LIGHT -> RoktSdkConfig.ColorMode.LIGHT - RoktConfig.ColorMode.DARK -> RoktSdkConfig.ColorMode.DARK - RoktConfig.ColorMode.SYSTEM -> RoktSdkConfig.ColorMode.SYSTEM - else -> RoktSdkConfig.ColorMode.SYSTEM - } - - val cacheConfig = this.cacheConfig?.toRoktSdkCacheConfig() - - val edgeToEdgeDisplay = this.edgeToEdgeDisplay - - val builder = RoktSdkConfig.Builder() - .colorMode(colorMode) - .edgeToEdgeDisplay(edgeToEdgeDisplay) - - cacheConfig?.let { builder.cacheConfig(it) } - - return builder.build() -} - -fun PlacementOptions.toRoktSdkPlacementOptions(): com.rokt.roktsdk.PlacementOptions = com.rokt.roktsdk.PlacementOptions( - jointSdkSelectPlacements = this.jointSdkSelectPlacements, - dynamicPerformanceMarkers = this.dynamicPerformanceMarkers, -) diff --git a/android-core/src/main/kotlin/com/mparticle/rokt/RoktEmbeddedView.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktEmbeddedView.kt similarity index 94% rename from android-core/src/main/kotlin/com/mparticle/rokt/RoktEmbeddedView.kt rename to kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktEmbeddedView.kt index 03db66366..59da59aae 100644 --- a/android-core/src/main/kotlin/com/mparticle/rokt/RoktEmbeddedView.kt +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktEmbeddedView.kt @@ -1,4 +1,4 @@ -package com.mparticle.rokt +package com.mparticle.kits import android.content.Context import android.util.AttributeSet diff --git a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKit.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKit.kt index 091ba6356..115c1dcc7 100644 --- a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKit.kt +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKit.kt @@ -10,8 +10,6 @@ import com.mparticle.BuildConfig import com.mparticle.MPEvent import com.mparticle.MParticle import com.mparticle.MParticle.IdentityType -import com.mparticle.MpRoktEventCallback -import com.mparticle.UnloadReasons import com.mparticle.WrapperSdk import com.mparticle.WrapperSdkVersion import com.mparticle.commerce.CommerceEvent @@ -20,15 +18,14 @@ import com.mparticle.internal.Logger import com.mparticle.kits.KitIntegration.CommerceListener import com.mparticle.kits.KitIntegration.IdentityListener import com.mparticle.kits.KitIntegration.RoktListener -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig -import com.mparticle.rokt.RoktEmbeddedView +import com.rokt.roktsdk.PlacementOptions import com.rokt.roktsdk.Rokt import com.rokt.roktsdk.Rokt.RoktCallback import com.rokt.roktsdk.Rokt.SdkFrameworkType.Android import com.rokt.roktsdk.Rokt.SdkFrameworkType.Cordova import com.rokt.roktsdk.Rokt.SdkFrameworkType.Flutter import com.rokt.roktsdk.Rokt.SdkFrameworkType.ReactNative +import com.rokt.roktsdk.RoktConfig import com.rokt.roktsdk.RoktEvent import com.rokt.roktsdk.RoktWidgetDimensionCallBack import com.rokt.roktsdk.Widget @@ -37,7 +34,6 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import java.lang.ref.WeakReference import java.math.BigDecimal @@ -56,9 +52,10 @@ class RoktKit : CommerceListener, IdentityListener, RoktListener, + RoktKitBridge, Rokt.RoktCallback { private var applicationContext: Context? = null - private var mpRoktEventCallback: MpRoktEventCallback? = null + private var roktCallback: RoktCallback? = null private var hashedEmailUserIdentityType: String? = null override fun getName(): String = NAME @@ -183,11 +180,11 @@ class RoktKit : override fun selectPlacements( viewName: String, attributes: Map, - mpRoktEventCallback: MpRoktEventCallback?, + roktCallback: RoktCallback?, placeHolders: MutableMap>?, fontTypefaces: MutableMap>?, filterUser: FilteredMParticleUser?, - mpRoktConfig: RoktConfig?, + roktConfig: RoktConfig?, placementOptions: PlacementOptions?, ) { val placeholders: Map>? = placeHolders?.mapNotNull { entry -> @@ -210,9 +207,8 @@ class RoktKit : entry.key to WeakReference(widget) }?.toMap() - this.mpRoktEventCallback = mpRoktEventCallback + this.roktCallback = roktCallback val finalAttributes = prepareFinalAttributes(filterUser, attributes) - val roktConfig = mpRoktConfig?.toRoktSdkConfig() Rokt.execute( viewName, finalAttributes, @@ -221,7 +217,7 @@ class RoktKit : placeholders.takeIf { it?.isNotEmpty() == true }, fontTypefaces.takeIf { it?.isNotEmpty() == true }, roktConfig, - placementOptions?.toRoktSdkPlacementOptions(), + placementOptions, ) } @@ -275,46 +271,7 @@ class RoktKit : return userAttributes } - override fun events(identifier: String): Flow = Rokt.events(identifier).map { event -> - when (event) { - is RoktEvent.HideLoadingIndicator -> com.mparticle.RoktEvent.HideLoadingIndicator - is RoktEvent.ShowLoadingIndicator -> com.mparticle.RoktEvent.ShowLoadingIndicator - is RoktEvent.FirstPositiveEngagement -> com.mparticle.RoktEvent.FirstPositiveEngagement( - event.id, - ) - - is RoktEvent.PositiveEngagement -> com.mparticle.RoktEvent.PositiveEngagement( - event.id, - ) - - is RoktEvent.OfferEngagement -> com.mparticle.RoktEvent.OfferEngagement(event.id) - is RoktEvent.OpenUrl -> com.mparticle.RoktEvent.OpenUrl(event.id, event.url) - is RoktEvent.PlacementClosed -> com.mparticle.RoktEvent.PlacementClosed(event.id) - is RoktEvent.PlacementCompleted -> com.mparticle.RoktEvent.PlacementCompleted( - event.id, - ) - - is RoktEvent.PlacementFailure -> com.mparticle.RoktEvent.PlacementFailure(event.id) - is RoktEvent.PlacementInteractive -> com.mparticle.RoktEvent.PlacementInteractive( - event.id, - ) - - is RoktEvent.PlacementReady -> com.mparticle.RoktEvent.PlacementReady(event.id) - is RoktEvent.CartItemInstantPurchase -> com.mparticle.RoktEvent.CartItemInstantPurchase( - placementId = event.placementId, - cartItemId = event.cartItemId, - catalogItemId = event.catalogItemId, - currency = event.currency, - description = event.description, - linkedProductId = event.linkedProductId, - totalPrice = event.totalPrice, - quantity = event.quantity, - unitPrice = event.unitPrice, - ) - - is RoktEvent.InitComplete -> com.mparticle.RoktEvent.InitComplete(event.success) - } - } + override fun events(identifier: String): Flow = Rokt.events(identifier) override fun setWrapperSdkVersion(wrapperSdkVersion: WrapperSdkVersion) { val sdkFrameworkType = when (wrapperSdkVersion.sdk) { @@ -359,13 +316,16 @@ class RoktKit : suspend fun runComposableWithCallback( attributes: Map, - mpRoktEventCallback: MpRoktEventCallback?, + roktCallback: RoktCallback?, onResult: (Map, RoktCallback) -> Unit, ) { - val instance = MParticle.getInstance() deferredAttributes = CompletableDeferred() - instance?.Internal()?.kitManager?.roktKitApi?.prepareAttributesAsync(attributes) - this.mpRoktEventCallback = mpRoktEventCallback + RoktKitRequestHelper.prepareAttributesAsync( + kitIntegration = this, + roktListener = this, + attributes = attributes, + ) + this.roktCallback = roktCallback CoroutineScope(Dispatchers.Default).launch { val resultAttributes = deferredAttributes!!.await() onResult(resultAttributes, this@RoktKit) @@ -469,30 +429,19 @@ class RoktKit : } override fun onLoad() { - mpRoktEventCallback?.onLoad() + roktCallback?.onLoad() } override fun onShouldHideLoadingIndicator() { - mpRoktEventCallback?.onShouldHideLoadingIndicator() + roktCallback?.onShouldHideLoadingIndicator() } override fun onShouldShowLoadingIndicator() { - mpRoktEventCallback?.onShouldShowLoadingIndicator() + roktCallback?.onShouldShowLoadingIndicator() } override fun onUnload(reason: Rokt.UnloadReasons) { - mpRoktEventCallback?.onUnload( - when (reason) { - Rokt.UnloadReasons.NO_OFFERS -> UnloadReasons.NO_OFFERS - Rokt.UnloadReasons.FINISHED -> UnloadReasons.FINISHED - Rokt.UnloadReasons.TIMEOUT -> UnloadReasons.TIMEOUT - Rokt.UnloadReasons.NETWORK_ERROR -> UnloadReasons.NETWORK_ERROR - Rokt.UnloadReasons.NO_WIDGET -> UnloadReasons.NO_WIDGET - Rokt.UnloadReasons.INIT_FAILED -> UnloadReasons.INIT_FAILED - Rokt.UnloadReasons.UNKNOWN_PLACEHOLDER -> UnloadReasons.UNKNOWN_PLACEHOLDER - Rokt.UnloadReasons.UNKNOWN -> UnloadReasons.UNKNOWN - }, - ) + roktCallback?.onUnload(reason) } } diff --git a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKitBridge.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKitBridge.kt new file mode 100644 index 000000000..00e99d555 --- /dev/null +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKitBridge.kt @@ -0,0 +1,34 @@ +package com.mparticle.kits + +import android.graphics.Typeface +import com.rokt.roktsdk.PlacementOptions +import com.rokt.roktsdk.Rokt.RoktCallback +import com.rokt.roktsdk.RoktConfig +import com.rokt.roktsdk.RoktEvent +import kotlinx.coroutines.flow.Flow +import java.lang.ref.WeakReference + +internal interface RoktKitBridge { + fun selectPlacements( + viewName: String, + attributes: Map, + roktCallback: RoktCallback?, + placeHolders: MutableMap>?, + fontTypefaces: MutableMap>?, + user: FilteredMParticleUser?, + config: RoktConfig?, + options: PlacementOptions?, + ) + + fun events(identifier: String): Flow + + fun enrichAttributes(attributes: MutableMap, user: FilteredMParticleUser?) + + fun purchaseFinalized(placementId: String, catalogItemId: String, status: Boolean) + + fun close() + + fun setSessionId(sessionId: String) + + fun getSessionId(): String? +} diff --git a/android-kit-base/src/main/kotlin/com/mparticle/kits/RoktKitApiImpl.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKitRequestHelper.kt similarity index 50% rename from android-kit-base/src/main/kotlin/com/mparticle/kits/RoktKitApiImpl.kt rename to kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKitRequestHelper.kt index 68fd73d4c..ba5778986 100644 --- a/android-kit-base/src/main/kotlin/com/mparticle/kits/RoktKitApiImpl.kt +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktKitRequestHelper.kt @@ -2,137 +2,81 @@ package com.mparticle.kits import android.graphics.Typeface import com.mparticle.MParticle -import com.mparticle.MpRoktEventCallback -import com.mparticle.RoktEvent import com.mparticle.identity.IdentityApi import com.mparticle.identity.IdentityApiRequest import com.mparticle.identity.MParticleUser -import com.mparticle.internal.Constants import com.mparticle.internal.Logger import com.mparticle.internal.MPUtility -import com.mparticle.internal.RoktKitApi -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig -import com.mparticle.rokt.RoktEmbeddedView -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf +import com.rokt.roktsdk.PlacementOptions +import com.rokt.roktsdk.Rokt.RoktCallback +import com.rokt.roktsdk.RoktConfig import org.json.JSONException import java.lang.ref.WeakReference import java.util.Objects -/** - * Implementation of [RoktKitApi] that wraps a [KitIntegration.RoktListener]. - * - * This class handles user resolution and attribute preparation before delegating - * to the underlying Rokt Kit implementation. - */ -internal class RoktKitApiImpl(private val roktListener: KitIntegration.RoktListener, private val kitIntegration: KitIntegration) : RoktKitApi { - - override fun selectPlacements( +internal object RoktKitRequestHelper { + fun selectPlacements( + kitIntegration: KitIntegration, + roktListener: RoktKitBridge, viewName: String, attributes: Map, - mpRoktEventCallback: MpRoktEventCallback?, + roktCallback: RoktCallback?, placeHolders: Map>?, fontTypefaces: Map>?, config: RoktConfig?, options: PlacementOptions?, ) { - try { - val mutableAttributes = attributes.toMutableMap() - val instance = MParticle.getInstance() - if (instance == null) { - Logger.warning("MParticle instance is null, cannot execute Rokt placement") - return - } - val user = instance.Identity().currentUser - val email = getValueIgnoreCase(mutableAttributes, "email") - val hashedEmail = getValueIgnoreCase(mutableAttributes, "emailsha256") - val kitConfig = kitIntegration.configuration - - confirmEmail(email, hashedEmail, user, instance.Identity(), kitConfig) { - val finalAttributes = prepareAttributes(mutableAttributes, user) - roktListener.selectPlacements( - viewName, - finalAttributes, - mpRoktEventCallback, - placeHolders, - fontTypefaces, - FilteredMParticleUser.getInstance(user?.id ?: 0L, kitIntegration), - config, - options, - ) - } - } catch (e: Exception) { - Logger.warning("Failed to call execute for Rokt Kit: ${e.message}") + val mutableAttributes = attributes.toMutableMap() + val instance = MParticle.getInstance() + if (instance == null) { + Logger.warning("MParticle instance is null, cannot execute Rokt placement") + return } - } - - override fun events(identifier: String): Flow = try { - Logger.verbose("Calling events for Rokt Kit with identifier: $identifier") - roktListener.events(identifier) - } catch (e: Exception) { - Logger.warning("Failed to call events for Rokt Kit: ${e.message}") - flowOf() - } - - override fun purchaseFinalized(placementId: String, catalogItemId: String, status: Boolean) { - try { - roktListener.purchaseFinalized(placementId, catalogItemId, status) - } catch (e: Exception) { - Logger.warning("Failed to call purchaseFinalized for Rokt Kit: ${e.message}") - } - } + val user = instance.Identity().currentUser + val email = getValueIgnoreCase(mutableAttributes, "email") + val hashedEmail = getValueIgnoreCase(mutableAttributes, "emailsha256") + val kitConfig = kitIntegration.configuration - override fun close() { - try { - roktListener.close() - } catch (e: Exception) { - Logger.warning("Failed to call close for Rokt Kit: ${e.message}") + confirmEmail(email, hashedEmail, user, instance.Identity(), kitConfig) { + val finalAttributes = prepareAttributes(mutableAttributes, user, kitConfig) + roktListener.selectPlacements( + viewName, + finalAttributes, + roktCallback, + placeHolders?.toMutableMap(), + fontTypefaces?.toMutableMap(), + FilteredMParticleUser.getInstance(user?.id ?: 0L, kitIntegration), + config, + options, + ) } } - override fun setSessionId(sessionId: String) { - try { - roktListener.setSessionId(sessionId) - } catch (e: Exception) { - Logger.warning("Failed to call setSessionId for Rokt Kit: ${e.message}") + fun prepareAttributesAsync( + kitIntegration: KitIntegration, + roktListener: RoktKitBridge, + attributes: Map, + ) { + val mutableAttributes = attributes.toMutableMap() + val instance = MParticle.getInstance() + if (instance == null) { + Logger.warning("MParticle instance is null, cannot prepare attributes") + return } - } - - override fun getSessionId(): String? = try { - roktListener.sessionId - } catch (e: Exception) { - Logger.warning("Failed to call getSessionId for Rokt Kit: ${e.message}") - null - } - - override fun prepareAttributesAsync(attributes: Map) { - try { - val mutableAttributes = attributes.toMutableMap() - val instance = MParticle.getInstance() - if (instance == null) { - Logger.warning("MParticle instance is null, cannot prepare attributes") - return - } - val user = instance.Identity().currentUser - val email = mutableAttributes["email"] - val hashedEmail = getValueIgnoreCase(mutableAttributes, "emailsha256") - val kitConfig = kitIntegration.configuration + val user = instance.Identity().currentUser + val email = mutableAttributes["email"] + val hashedEmail = getValueIgnoreCase(mutableAttributes, "emailsha256") + val kitConfig = kitIntegration.configuration - confirmEmail(email, hashedEmail, user, instance.Identity(), kitConfig) { - val finalAttributes = prepareAttributes(mutableAttributes, user) - roktListener.enrichAttributes( - finalAttributes, - FilteredMParticleUser.getInstance(user?.id ?: 0L, kitIntegration), - ) - } - } catch (e: Exception) { - Logger.warning("Failed to call prepareAttributesAsync for Rokt Kit: ${e.message}") + confirmEmail(email, hashedEmail, user, instance.Identity(), kitConfig) { + val finalAttributes = prepareAttributes(mutableAttributes, user, kitConfig) + roktListener.enrichAttributes( + finalAttributes, + FilteredMParticleUser.getInstance(user?.id ?: 0L, kitIntegration), + ) } } - // Helper methods - private fun getValueIgnoreCase(map: Map, searchKey: String): String? { for ((key, value) in map) { if (key.equals(searchKey, ignoreCase = true)) { @@ -142,8 +86,11 @@ internal class RoktKitApiImpl(private val roktListener: KitIntegration.RoktListe return null } - private fun prepareAttributes(finalAttributes: MutableMap, user: MParticleUser?): MutableMap { - val kitConfig = kitIntegration.configuration + private fun prepareAttributes( + finalAttributes: MutableMap, + user: MParticleUser?, + kitConfig: KitConfiguration?, + ): MutableMap { val jsonArray = try { kitConfig?.placementAttributesMapping ?: org.json.JSONArray() } catch (e: JSONException) { @@ -165,14 +112,14 @@ internal class RoktKitApiImpl(private val roktListener: KitIntegration.RoktListe val objectAttributes = mutableMapOf() for ((key, value) in finalAttributes) { - if (key != Constants.MessageKey.SANDBOX_MODE_ROKT) { + if (key != ROKT_ATTRIBUTE_SANDBOX_MODE) { objectAttributes[key] = value } } user?.setUserAttributes(objectAttributes) - if (!finalAttributes.containsKey(Constants.MessageKey.SANDBOX_MODE_ROKT)) { - finalAttributes[Constants.MessageKey.SANDBOX_MODE_ROKT] = + if (!finalAttributes.containsKey(ROKT_ATTRIBUTE_SANDBOX_MODE)) { + finalAttributes[ROKT_ATTRIBUTE_SANDBOX_MODE] = Objects.toString(MPUtility.isDevEnv(), "false") } return finalAttributes @@ -207,20 +154,27 @@ internal class RoktKitApiImpl(private val roktListener: KitIntegration.RoktListe hasHashedEmail && !hashedEmail.equals(existingHashedEmail, ignoreCase = true) if (emailMismatch || (hashedEmailMismatch && selectedIdentityType != null)) { - // If there's an existing email but it doesn't match the passed-in email, log a warning if (emailMismatch && existingEmail != null) { + val emailMismatchMessage = + "The existing email on the user ($existingEmail) does not match " + + "the email passed to selectPlacements ($email). " + + "Please make sure to sync the email identity to mParticle " + + "as soon as it's available. " + + "Identifying user with the provided email before continuing " + + "to selectPlacements." Logger.warning( - "The existing email on the user ($existingEmail) does not match the email passed to selectPlacements ($email). " + - "Please make sure to sync the email identity to mParticle as soon as it's available. " + - "Identifying user with the provided email before continuing to selectPlacements.", + emailMismatchMessage, ) } else if (hashedEmailMismatch && existingHashedEmail != null) { - // If there's an existing other but it doesn't match the passed-in hashed email, log a warning - Logger.warning( + val hashedEmailMismatchMessage = "The existing hashed email on the user ($existingHashedEmail) does not match " + "the hashed email passed to selectPlacements ($hashedEmail). " + - "Please make sure to sync the hashed email identity to mParticle as soon as it's available. " + - "Identifying user with the provided hashed email before continuing to selectPlacements.", + "Please make sure to sync the hashed email identity to mParticle " + + "as soon as it's available. " + + "Identifying user with the provided hashed email before continuing " + + "to selectPlacements." + Logger.warning( + hashedEmailMismatchMessage, ) } diff --git a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktLayout.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktLayout.kt index 0987ea40d..6469aea2c 100644 --- a/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktLayout.kt +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktLayout.kt @@ -5,10 +5,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import com.mparticle.MpRoktEventCallback -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig +import com.rokt.roktsdk.PlacementOptions import com.rokt.roktsdk.Rokt +import com.rokt.roktsdk.RoktConfig @Composable @Suppress("FunctionName") @@ -18,7 +17,7 @@ fun RoktLayout( attributes: Map, location: String, modifier: Modifier = Modifier, - mpRoktEventCallback: MpRoktEventCallback? = null, + roktCallback: Rokt.RoktCallback? = null, config: RoktConfig? = null, ) { var placementOptions: PlacementOptions? = null @@ -33,7 +32,7 @@ fun RoktLayout( LaunchedEffect(Unit) { instance?.runComposableWithCallback( HashMap(attributes), - mpRoktEventCallback, + roktCallback, { resultMap, callback -> resultMapState.value = RoktResult(resultMap, callback) }, @@ -48,8 +47,8 @@ fun RoktLayout( onShouldShowLoadingIndicator = { resultMap.callback.onShouldShowLoadingIndicator() }, onShouldHideLoadingIndicator = { resultMap.callback.onShouldHideLoadingIndicator() }, onUnload = { reason -> resultMap.callback.onUnload(reason) }, - config = config?.toRoktSdkConfig(), - placementOptions = placementOptions?.toRoktSdkPlacementOptions(), + config = config, + placementOptions = placementOptions, ) } } diff --git a/android-core/src/main/kotlin/com/mparticle/rokt/RoktLayoutDimensionCallBack.kt b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktLayoutDimensionCallBack.kt similarity index 84% rename from android-core/src/main/kotlin/com/mparticle/rokt/RoktLayoutDimensionCallBack.kt rename to kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktLayoutDimensionCallBack.kt index e22535c90..5dffc4f67 100644 --- a/android-core/src/main/kotlin/com/mparticle/rokt/RoktLayoutDimensionCallBack.kt +++ b/kits/rokt/rokt/src/main/kotlin/com/mparticle/kits/RoktLayoutDimensionCallBack.kt @@ -1,4 +1,4 @@ -package com.mparticle.rokt +package com.mparticle.kits interface RoktLayoutDimensionCallBack { fun onHeightChanged(height: Int) diff --git a/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktConfigExtensionsTest.kt b/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktConfigExtensionsTest.kt deleted file mode 100644 index 14a407c99..000000000 --- a/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktConfigExtensionsTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.mparticle.kits - -import com.mparticle.rokt.CacheConfig -import com.mparticle.rokt.RoktConfig -import io.mockk.every -import io.mockk.mockk -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Test -import com.rokt.roktsdk.RoktConfig as SdkRoktConfig - -class RoktConfigExtensionsTest { - - @Test - fun `toRoktSdkConfig maps color mode and edgeToEdge`() { - val source = mockk() - every { source.colorMode } returns RoktConfig.ColorMode.DARK - every { source.edgeToEdgeDisplay } returns true - every { source.cacheConfig } returns null - - val result: SdkRoktConfig = source.toRoktSdkConfig() - - assertEquals(SdkRoktConfig.ColorMode.DARK, result.colorMode) - assertEquals(true, result.edgeToEdgeDisplay) - } - - @Test - fun `toRoktSdkConfig maps cacheConfig when present`() { - val cacheAttributes = mapOf( - "key1" to "value1", - "key2" to "value2", - ) - - val cacheConfig = mockk() - every { cacheConfig.cacheDurationInSeconds } returns 3600 - every { cacheConfig.cacheAttributes } returns cacheAttributes - - val source = mockk() - every { source.colorMode } returns RoktConfig.ColorMode.LIGHT - every { source.edgeToEdgeDisplay } returns false - every { source.cacheConfig } returns cacheConfig - - val result: SdkRoktConfig = source.toRoktSdkConfig() - - assertEquals(SdkRoktConfig.ColorMode.LIGHT, result.colorMode) - assertEquals(false, result.edgeToEdgeDisplay) - assertNotNull(result.cacheConfig) - assertEquals(3600L, result.cacheConfig?.cacheDurationInSeconds) - assertEquals(cacheAttributes, result.cacheConfig?.cacheAttributes) - } - - @Test - fun `toRoktSdkCacheConfig maps fields`() { - val cacheAttributes = mapOf( - "a" to "1", - "b" to "2", - ) - val mpCache = mockk() - every { mpCache.cacheDurationInSeconds } returns 120 - every { mpCache.cacheAttributes } returns cacheAttributes - - val sdkCache = mpCache.toRoktSdkCacheConfig() - - assertEquals(120, sdkCache.cacheDurationInSeconds) - assertEquals(cacheAttributes, sdkCache.cacheAttributes) - } -} diff --git a/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt b/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt index 9717978a6..ec8cbbffd 100644 --- a/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt +++ b/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt @@ -160,11 +160,11 @@ class RoktKitTests { roktKit.selectPlacements( viewName = "test_view", attributes = inputAttributes, - mpRoktEventCallback = null, + roktCallback = null, placeHolders = null, fontTypefaces = null, filterUser = mockFilterUser, - mpRoktConfig = null, + roktConfig = null, placementOptions = null, ) @@ -217,11 +217,11 @@ class RoktKitTests { roktKit.selectPlacements( viewName = "test_view", attributes = emptyMap(), - mpRoktEventCallback = null, + roktCallback = null, placeHolders = null, fontTypefaces = null, filterUser = mockFilterUser, - mpRoktConfig = null, + roktConfig = null, placementOptions = null, ) @@ -266,11 +266,11 @@ class RoktKitTests { roktKit.selectPlacements( viewName = "test_view", attributes = emptyMap(), - mpRoktEventCallback = null, + roktCallback = null, placeHolders = null, fontTypefaces = null, filterUser = mockFilterUser, - mpRoktConfig = null, + roktConfig = null, placementOptions = null, ) @@ -315,11 +315,11 @@ class RoktKitTests { roktKit.selectPlacements( viewName = "test_view", attributes = emptyMap(), - mpRoktEventCallback = null, + roktCallback = null, placeHolders = null, fontTypefaces = null, filterUser = mockFilterUser, - mpRoktConfig = null, + roktConfig = null, placementOptions = null, ) @@ -382,11 +382,11 @@ class RoktKitTests { roktKit.selectPlacements( viewName = "test", attributes = inputAttributes, - mpRoktEventCallback = null, + roktCallback = null, placeHolders = null, fontTypefaces = null, filterUser = mockFilterUser, - mpRoktConfig = null, + roktConfig = null, placementOptions = null, ) @@ -457,11 +457,11 @@ class RoktKitTests { roktKit.selectPlacements( viewName = "test", attributes = inputAttributes, - mpRoktEventCallback = null, + roktCallback = null, placeHolders = null, fontTypefaces = null, filterUser = mockFilterUser, - mpRoktConfig = null, + roktConfig = null, placementOptions = null, ) @@ -761,31 +761,31 @@ class RoktKitTests { } @Test - fun testRoktEventMapping_ShowLoadingIndicator() = runTest { + fun testRoktEvents_ShowLoadingIndicator() = runTest { mockkObject(Rokt) val roktEvent = RoktEvent.ShowLoadingIndicator every { Rokt.events(any()) } returns flowOf(roktEvent) val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.ShowLoadingIndicator) + assertEquals(result, RoktEvent.ShowLoadingIndicator) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_HideLoadingIndicator() = runTest { + fun testRoktEvents_HideLoadingIndicator() = runTest { mockkObject(Rokt) val roktEvent = RoktEvent.HideLoadingIndicator every { Rokt.events(any()) } returns flowOf(roktEvent) val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.HideLoadingIndicator) + assertEquals(result, RoktEvent.HideLoadingIndicator) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_FirstPositiveEngagement() = runTest { + fun testRoktEvents_FirstPositiveEngagement() = runTest { mockkObject(Rokt) val placementId = "test-placement-123" val roktEvent = RoktEvent.FirstPositiveEngagement( @@ -799,12 +799,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.FirstPositiveEngagement(placementId)) + assertEquals(placementId, (result as RoktEvent.FirstPositiveEngagement).id) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_PositiveEngagement() = runTest { + fun testRoktEvents_PositiveEngagement() = runTest { mockkObject(Rokt) val placementId = "test-placement-456" val roktEvent = RoktEvent.PositiveEngagement(placementId) @@ -812,12 +812,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.PositiveEngagement(placementId)) + assertEquals(result, RoktEvent.PositiveEngagement(placementId)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_OfferEngagement() = runTest { + fun testRoktEvents_OfferEngagement() = runTest { mockkObject(Rokt) val placementId = "test-placement-789" val roktEvent = RoktEvent.OfferEngagement(placementId) @@ -825,12 +825,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.OfferEngagement(placementId)) + assertEquals(result, RoktEvent.OfferEngagement(placementId)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_OpenUrl() = runTest { + fun testRoktEvents_OpenUrl() = runTest { mockkObject(Rokt) val placementId = "test-placement-url" val url = "https://example.com" @@ -839,12 +839,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.OpenUrl(placementId, url)) + assertEquals(result, RoktEvent.OpenUrl(placementId, url)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_PlacementClosed() = runTest { + fun testRoktEvents_PlacementClosed() = runTest { mockkObject(Rokt) val placementId = "test-placement-closed" val roktEvent = RoktEvent.PlacementClosed(placementId) @@ -852,12 +852,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.PlacementClosed(placementId)) + assertEquals(result, RoktEvent.PlacementClosed(placementId)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_PlacementCompleted() = runTest { + fun testRoktEvents_PlacementCompleted() = runTest { mockkObject(Rokt) val placementId = "test-placement-completed" val roktEvent = RoktEvent.PlacementCompleted(placementId) @@ -865,12 +865,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.PlacementCompleted(placementId)) + assertEquals(result, RoktEvent.PlacementCompleted(placementId)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_PlacementFailure() = runTest { + fun testRoktEvents_PlacementFailure() = runTest { mockkObject(Rokt) val placementId = "test-placement-failure" val roktEvent = RoktEvent.PlacementFailure(placementId) @@ -878,12 +878,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.PlacementFailure(placementId)) + assertEquals(result, RoktEvent.PlacementFailure(placementId)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_PlacementInteractive() = runTest { + fun testRoktEvents_PlacementInteractive() = runTest { mockkObject(Rokt) val placementId = "test-placement-interactive" val roktEvent = RoktEvent.PlacementInteractive(placementId) @@ -891,12 +891,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.PlacementInteractive(placementId)) + assertEquals(result, RoktEvent.PlacementInteractive(placementId)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_PlacementReady() = runTest { + fun testRoktEvents_PlacementReady() = runTest { mockkObject(Rokt) val placementId = "test-placement-ready" val roktEvent = RoktEvent.PlacementReady(placementId) @@ -904,12 +904,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.PlacementReady(placementId)) + assertEquals(result, RoktEvent.PlacementReady(placementId)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_InitComplete() = runTest { + fun testRoktEvents_InitComplete() = runTest { mockkObject(Rokt) val success = true val roktEvent = RoktEvent.InitComplete(success) @@ -917,12 +917,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.InitComplete(success)) + assertEquals(result, RoktEvent.InitComplete(success)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_CartItemInstantPurchase() = runTest { + fun testRoktEvents_CartItemInstantPurchase() = runTest { mockkObject(Rokt) val roktEvent = RoktEvent.CartItemInstantPurchase( placementId = "test-placement-purchase", @@ -941,7 +941,7 @@ class RoktKitTests { assertEquals( result, - com.mparticle.RoktEvent.CartItemInstantPurchase( + RoktEvent.CartItemInstantPurchase( placementId = "test-placement-purchase", cartItemId = "cart-item-123", catalogItemId = "catalog-item-456", @@ -957,19 +957,19 @@ class RoktKitTests { } @Test - fun testRoktEventMapping_PlacementFailureWithNullId() = runTest { + fun testRoktEvents_PlacementFailureWithNullId() = runTest { mockkObject(Rokt) val roktEvent = RoktEvent.PlacementFailure(null) every { Rokt.events(any()) } returns flowOf(roktEvent) val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.PlacementFailure(null)) + assertEquals(result, RoktEvent.PlacementFailure(null)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_InitCompleteWithFailure() = runTest { + fun testRoktEvents_InitCompleteWithFailure() = runTest { mockkObject(Rokt) val success = false val roktEvent = RoktEvent.InitComplete(success) @@ -977,12 +977,12 @@ class RoktKitTests { val result = roktKit.events("").first() - assertEquals(result, com.mparticle.RoktEvent.InitComplete(success)) + assertEquals(result, RoktEvent.InitComplete(success)) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_MultipleEventsInFlow() = runTest { + fun testRoktEvents_MultipleEventsInFlow() = runTest { mockkObject(Rokt) val events = listOf( RoktEvent.ShowLoadingIndicator, @@ -994,15 +994,15 @@ class RoktKitTests { val results = roktKit.events("").toList() assertEquals(3, results.size) - assertEquals(com.mparticle.RoktEvent.ShowLoadingIndicator, results[0]) - assertEquals(com.mparticle.RoktEvent.HideLoadingIndicator, results[1]) - assertEquals(com.mparticle.RoktEvent.InitComplete(true), results[2]) + assertEquals(RoktEvent.ShowLoadingIndicator, results[0]) + assertEquals(RoktEvent.HideLoadingIndicator, results[1]) + assertEquals(RoktEvent.InitComplete(true), results[2]) unmockkObject(Rokt) } @Test - fun testRoktEventMapping_EmptyFlow() = runTest { + fun testRoktEvents_EmptyFlow() = runTest { mockkObject(Rokt) every { Rokt.events(any()) } returns flowOf() @@ -1013,7 +1013,7 @@ class RoktKitTests { } @Test - fun testRoktEventMapping_WithDifferentIdentifiers() = runTest { + fun testRoktEvents_WithDifferentIdentifiers() = runTest { mockkObject(Rokt) val identifier1 = "test-identifier-1" val identifier2 = "test-identifier-2" @@ -1024,8 +1024,8 @@ class RoktKitTests { val result1 = roktKit.events(identifier1).first() val result2 = roktKit.events(identifier2).first() - assertEquals(com.mparticle.RoktEvent.ShowLoadingIndicator, result1) - assertEquals(com.mparticle.RoktEvent.HideLoadingIndicator, result2) + assertEquals(RoktEvent.ShowLoadingIndicator, result1) + assertEquals(RoktEvent.HideLoadingIndicator, result2) verify { Rokt.events(identifier1) } verify { Rokt.events(identifier2) } @@ -1358,11 +1358,11 @@ class RoktKitTests { roktKit.selectPlacements( viewName = "test_view", attributes = testAttributes, - mpRoktEventCallback = null, + roktCallback = null, placeHolders = null, fontTypefaces = null, filterUser = mockFilterUser, - mpRoktConfig = null, + roktConfig = null, placementOptions = null, ) diff --git a/android-core/src/test/kotlin/com/mparticle/RoktTest.kt b/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktTest.kt similarity index 66% rename from android-core/src/test/kotlin/com/mparticle/RoktTest.kt rename to kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktTest.kt index 317f58626..4ca6d857a 100644 --- a/android-core/src/test/kotlin/com/mparticle/RoktTest.kt +++ b/kits/rokt/rokt/src/test/kotlin/com/mparticle/kits/RoktTest.kt @@ -1,14 +1,15 @@ -package com.mparticle +package com.mparticle.kits import android.graphics.Typeface import android.os.Looper import android.os.SystemClock -import com.mparticle.internal.ConfigManager +import com.mparticle.MParticle +import com.mparticle.identity.IdentityApi +import com.mparticle.identity.MParticleUser import com.mparticle.internal.KitManager -import com.mparticle.internal.RoktKitApi -import com.mparticle.rokt.PlacementOptions -import com.mparticle.rokt.RoktConfig -import com.mparticle.rokt.RoktEmbeddedView +import com.rokt.roktsdk.PlacementOptions +import com.rokt.roktsdk.RoktConfig +import com.rokt.roktsdk.RoktEvent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList @@ -23,6 +24,7 @@ import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` +import org.mockito.Mockito.withSettings import org.mockito.MockitoAnnotations import org.powermock.core.classloader.annotations.PrepareForTest import org.powermock.modules.junit4.PowerMockRunner @@ -38,12 +40,24 @@ class RoktTest { lateinit var kitManager: KitManager @Mock - lateinit var roktKitApi: RoktKitApi + lateinit var identityApi: IdentityApi @Mock - lateinit var configManager: ConfigManager + lateinit var mParticle: MParticle + + @Mock + lateinit var mParticleUser: MParticleUser + + private lateinit var roktKit: KitIntegration + private lateinit var roktListener: RoktKitBridge + + private lateinit var configManager: FakeConfigManager private lateinit var rokt: Rokt + class FakeConfigManager(var enabled: Boolean = true) { + fun isEnabled(): Boolean = enabled + } + // Helpers to make Mockito matchers work in Kotlin with non-nullable types. // Mockito matchers return null, which Kotlin rejects for non-nullable params. // These helpers call the matcher (to register it) then return a cast null. @@ -68,13 +82,25 @@ class RoktTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) - rokt = Rokt(configManager, kitManager) + configManager = FakeConfigManager(enabled = true) + roktKit = + org.mockito.Mockito.mock( + KitIntegration::class.java, + withSettings().extraInterfaces(RoktKitBridge::class.java), + ) + roktListener = roktKit as RoktKitBridge + MParticle.setInstance(mParticle) + `when`(mParticle.Identity()).thenReturn(identityApi) + `when`(identityApi.currentUser).thenReturn(mParticleUser) + `when`(kitManager.isKitActive(MParticle.ServiceProviders.ROKT)).thenReturn(true) + `when`(kitManager.getKitInstance(MParticle.ServiceProviders.ROKT)).thenReturn(roktKit) + `when`(kitManager.isEnabled).thenAnswer { configManager.enabled } + rokt = Rokt(kitManager) } @Test fun testSelectPlacements_withFullParams_whenEnabled() { - `when`(configManager.isEnabled).thenReturn(true) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = true val attributes = mutableMapOf() attributes["key"] = "value" @@ -85,12 +111,12 @@ class RoktTest { val config = RoktConfig.Builder().colorMode(RoktConfig.ColorMode.DARK).build() val callbacks = - object : MpRoktEventCallback { + object : com.rokt.roktsdk.Rokt.RoktCallback { override fun onLoad() { println("View loaded") } - override fun onUnload(reason: UnloadReasons) { + override fun onUnload(reason: com.rokt.roktsdk.Rokt.UnloadReasons) { println("View unloaded due to: $reason") } @@ -111,9 +137,10 @@ class RoktTest { config = config, ) - verify(roktKitApi).selectPlacements( + verify(roktListener).selectPlacements( eq("testView"), - eq(attributes), + any(), + any(), any(), any(), any(), @@ -124,41 +151,40 @@ class RoktTest { @Test fun testSelectPlacements_withBasicParams_whenEnabled() { - `when`(configManager.isEnabled()).thenReturn(true) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = true val attributes = mutableMapOf() attributes["a"] = "b" rokt.selectPlacements(attributes = attributes, identifier = "basicView") - verify(roktKitApi).selectPlacements( + verify(roktListener).selectPlacements( eq("basicView"), - eq(attributes), + any(), isNull(), isNull(), isNull(), isNull(), any(), + any(), ) } @Test fun testSelectPlacements_withBasicParams_whenDisabled() { - `when`(configManager.isEnabled()).thenReturn(false) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = false rokt.selectPlacements( identifier = "basicView", attributes = HashMap(), ) - verify(roktKitApi, never()).selectPlacements(any(), any(), any(), any(), any(), any(), any()) + verify(roktListener, never()).selectPlacements(any(), any(), any(), any(), any(), any(), any(), any()) } @Test fun testRoktSetWrapperSdk_whenDisabled_kitManagerNotCalled() { - `when`(configManager.isEnabled()).thenReturn(false) + configManager.enabled = false rokt.selectPlacements( identifier = "basicView", @@ -170,52 +196,48 @@ class RoktTest { @Test fun testReportConversion_withBasicParams_whenEnabled() { - `when`(configManager.isEnabled()).thenReturn(true) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = true val attributes = mutableMapOf() attributes["a"] = "b" rokt.purchaseFinalized("132", "1111", true) - verify(roktKitApi).purchaseFinalized("132", "1111", true) + verify(roktListener).purchaseFinalized("132", "1111", true) } @Test fun testReportConversion_withBasicParams_whenDisabled() { - `when`(configManager.isEnabled()).thenReturn(false) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = false rokt.purchaseFinalized("132", "1111", true) - verify(roktKitApi, never()).purchaseFinalized("132", "1111", true) + verify(roktListener, never()).purchaseFinalized("132", "1111", true) } @Test fun testEvents_whenEnabled_delegatesToKitManager() { - `when`(configManager.isEnabled).thenReturn(true) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = true val testIdentifier = "test-identifier" val expectedFlow: Flow = flowOf() - `when`(roktKitApi.events(testIdentifier)).thenReturn(expectedFlow) + `when`(roktListener.events(testIdentifier)).thenReturn(expectedFlow) val result = rokt.events(testIdentifier) - verify(roktKitApi).events(testIdentifier) + verify(roktListener).events(testIdentifier) assertEquals(expectedFlow, result) } @Test fun testEvents_whenDisabled_returnsEmptyFlow() { - `when`(configManager.isEnabled).thenReturn(false) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = false val testIdentifier = "test-identifier" val result = rokt.events(testIdentifier) - verify(roktKitApi, never()).events(any()) + verify(roktListener, never()).events(any()) runTest { val elements = result.toList() assertTrue(elements.isEmpty()) @@ -224,43 +246,38 @@ class RoktTest { @Test fun testSetSessionId_whenEnabled_delegatesToKitManager() { - `when`(configManager.isEnabled).thenReturn(true) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = true rokt.setSessionId("test-session-id") - verify(roktKitApi).setSessionId("test-session-id") + verify(roktListener).setSessionId("test-session-id") } @Test fun testSetSessionId_whenDisabled_doesNotCallKitManager() { - `when`(configManager.isEnabled).thenReturn(false) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = false rokt.setSessionId("test-session-id") - verify(roktKitApi, never()).setSessionId(any()) + verify(roktListener, never()).setSessionId(any()) } @Test fun testGetSessionId_whenEnabled_delegatesToKitManager() { - `when`(configManager.isEnabled).thenReturn(true) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) - `when`(roktKitApi.getSessionId()).thenReturn("expected-session-id") + configManager.enabled = true + `when`(roktListener.getSessionId()).thenReturn("expected-session-id") val result = rokt.getSessionId() - verify(roktKitApi).getSessionId() + verify(roktListener).getSessionId() assertEquals("expected-session-id", result) } @Test fun testGetSessionId_whenDisabled_returnsNull() { - `when`(configManager.isEnabled).thenReturn(false) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = false val result = rokt.getSessionId() - verify(roktKitApi, never()).getSessionId() + verify(roktListener, never()).getSessionId() assertNull(result) } @Test fun testSelectPlacements_withOptions_whenEnabled() { - `when`(configManager.isEnabled).thenReturn(true) - `when`(kitManager.roktKitApi).thenReturn(roktKitApi) + configManager.enabled = true val currentTimeMillis = System.currentTimeMillis() val attributes = mutableMapOf() @@ -271,14 +288,14 @@ class RoktTest { ) // Verify call is forwarded - val viewNameCaptor = ArgumentCaptor.forClass(String::class.java) val optionsCaptor = ArgumentCaptor.forClass(PlacementOptions::class.java) - verify(roktKitApi).selectPlacements( + verify(roktListener).selectPlacements( eq("testView"), any(), isNull(), isNull(), isNull(), + any(), isNull(), capture(optionsCaptor), )