Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android-kit-base/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ android {
tasks.withType(Test).configureEach {
jvmArgs('--add-opens=java.base/java.lang=ALL-UNNAMED')
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
jvmArgs("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED")
jvmArgs('--add-opens=java.base/java.util.concurrent=ALL-UNNAMED')
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.mparticle.kits;

import static android.util.Log.e;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
Expand All @@ -10,6 +12,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
Expand All @@ -23,12 +26,18 @@
import com.mparticle.MPEvent;
import com.mparticle.MParticle;
import com.mparticle.MParticleOptions;
import com.mparticle.MParticleTask;
import com.mparticle.UserAttributeListener;
import com.mparticle.commerce.CommerceEvent;
import com.mparticle.consent.ConsentState;
import com.mparticle.identity.IdentityApi;
import com.mparticle.identity.IdentityApiRequest;
import com.mparticle.identity.IdentityApiResult;
import com.mparticle.identity.IdentityHttpResponse;
import com.mparticle.identity.IdentityStateListener;
import com.mparticle.identity.MParticleUser;
import com.mparticle.identity.TaskFailureListener;
import com.mparticle.identity.TaskSuccessListener;
import com.mparticle.internal.CoreCallbacks;
import com.mparticle.internal.KitManager;
import com.mparticle.internal.KitsLoadedCallback;
Expand All @@ -54,6 +63,7 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

public class KitManagerImpl implements KitManager, AttributionListener, UserAttributeListener, IdentityStateListener {

Expand Down Expand Up @@ -1330,8 +1340,12 @@ public void execute(String viewName,
for (KitIntegration provider : providers.values()) {
try {
if (provider instanceof KitIntegration.RoktListener && !provider.isDisabled()) {
MParticleUser user = MParticle.getInstance().Identity().getCurrentUser();
MParticle instance = MParticle.getInstance();
MParticleUser user = instance.Identity().getCurrentUser();
String email = attributes != null ? attributes.get("email") : null;
confirmEmail(email,user,instance.Identity(), () -> {
JSONArray jsonArray = new JSONArray();

KitConfiguration kitConfig = provider.getConfiguration();
if (kitConfig != null) {
try {
Expand Down Expand Up @@ -1361,13 +1375,61 @@ public void execute(String viewName,
placeHolders,
fontTypefaces,
FilteredMParticleUser.getInstance(user.getId(), provider));
});
}
} catch (Exception e) {
Logger.warning("Failed to call execute for kit: " + provider.getName() + ": " + e.getMessage());
}
}
}

private void confirmEmail(
@Nullable String email,
@Nullable MParticleUser user,
IdentityApi identityApi,
Runnable runnable
) {
if (email != null && user != null) {
String existingEmail = user.getUserIdentities().get(MParticle.IdentityType.Email);

if (!email.equals(existingEmail)) {
// If there's an existing email but it doesn't match the passed-in email, log a warning
if (existingEmail != null) {
Logger.warning( String.format(
"The existing email on the user (%s) does not match the email passed to selectPlacements (%s). " +
"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.",
existingEmail, email
));
}

IdentityApiRequest identityRequest = IdentityApiRequest.withUser(user)
.email(email)
.build();
MParticleTask<IdentityApiResult> task = identityApi.identify(identityRequest);
task.addFailureListener(new TaskFailureListener() {
@Override
public void onFailure(IdentityHttpResponse result) {
Logger.error( "Failed to sync email from selectPlacement to user: " + result.getErrors().toString());

runnable.run();
}
});
task.addSuccessListener(new TaskSuccessListener() {
@Override
public void onSuccess(IdentityApiResult result) {
Logger.debug("Updated email identity based on selectPlacement's attributes: " + result.getUser().getUserIdentities().get(MParticle.IdentityType.Email));
runnable.run();
}
});
} else {
runnable.run();
}
} else {
runnable.run();
}
}

public void runOnKitThread(Runnable runnable) {
if (mKitHandler == null) {
mKitHandler = new Handler(kitHandlerThread.getLooper());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ 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.MParticleTask
import com.mparticle.commerce.CommerceEvent
import com.mparticle.commerce.Product
import com.mparticle.consent.ConsentState
import com.mparticle.consent.GDPRConsent
import com.mparticle.identity.IdentityApi
import com.mparticle.identity.IdentityApiResult
import com.mparticle.identity.MParticleUser
import com.mparticle.internal.CoreCallbacks
import com.mparticle.internal.SideloadedKit
Expand All @@ -28,18 +32,31 @@ import org.json.JSONObject
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
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.lang.reflect.Method
import java.util.Arrays
import java.util.LinkedList
import java.util.concurrent.ConcurrentHashMap

@RunWith(PowerMockRunner::class)
@PrepareForTest(Looper::class, SystemClock::class)
class KitManagerImplTest {
var mparticle: MParticle? = null
var mockIdentity: IdentityApi? = null

@Before
fun before() {
PowerMockito.mockStatic(Looper::class.java)
PowerMockito.mockStatic(SystemClock::class.java)
mockIdentity = Mockito.mock(IdentityApi::class.java)
val instance = MockMParticle()
instance.setIdentityApi(mockIdentity)
Expand Down Expand Up @@ -1023,6 +1040,194 @@ class KitManagerImplTest {
Assert.assertEquals("US", attributes["country"])
}

@Test
fun testConfirmEmail_When_EmailSyncSuccess() {
var runnable: Runnable = Mockito.mock(Runnable::class.java)
var user: MParticleUser = Mockito.mock(MParticleUser::class.java)
val instance = MockMParticle()
val sideloadedKit = Mockito.mock(MPSideloadedKit::class.java)
val kitId = 6000000

val configJSONObj = JSONObject().apply {
put("id", kitId)
}
val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj)
Mockito.`when`(sideloadedKit.configuration).thenReturn(mockedKitConfig)
val identityApi = mock(IdentityApi::class.java)
val oldEmail = "old@example.com"
val mockTask = mock(MParticleTask::class.java) as MParticleTask<IdentityApiResult>
`when`(identityApi.identify(any())).thenReturn(mockTask)
val identities: MutableMap<MParticle.IdentityType, String> = HashMap()
identities.put(MParticle.IdentityType.Email, oldEmail)
`when`(user.userIdentities).thenReturn(identities)
instance.setIdentityApi(identityApi)
val settingsMap = hashMapOf(
"placementAttributesMapping" to """
[

]
""".trimIndent()
)
val field = KitConfiguration::class.java.getDeclaredField("settings")
field.isAccessible = true
field.set(mockedKitConfig, settingsMap)

val options = MParticleOptions.builder(MockContext())
.sideloadedKits(mutableListOf(sideloadedKit) as List<SideloadedKit>).build()
val manager: KitManagerImpl = MockKitManagerImpl(options)
val method: Method = KitManagerImpl::class.java.getDeclaredMethod(
"confirmEmail",
String::class.java,
MParticleUser::class.java,
IdentityApi::class.java,
Runnable::class.java
)
method.isAccessible = true
val result = method.invoke(manager, "Test@gmail.com", user, identityApi, runnable)
verify(mockTask).addSuccessListener(any())
}

@Test
fun testConfirmEmail_When_EmailAlreadySynced() {
var runnable: Runnable = Mockito.mock(Runnable::class.java)
var user: MParticleUser = Mockito.mock(MParticleUser::class.java)
val instance = MockMParticle()
val sideloadedKit = Mockito.mock(MPSideloadedKit::class.java)
val kitId = 6000000

val configJSONObj = JSONObject().apply {
put("id", kitId)
}
val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj)
Mockito.`when`(sideloadedKit.configuration).thenReturn(mockedKitConfig)
val identityApi = mock(IdentityApi::class.java)
val oldEmail = "Test@gmail.com"
val mockTask = mock(MParticleTask::class.java) as MParticleTask<IdentityApiResult>
`when`(identityApi.identify(any())).thenReturn(mockTask)
val identities: MutableMap<MParticle.IdentityType, String> = HashMap()
identities.put(MParticle.IdentityType.Email, oldEmail)
`when`(user.userIdentities).thenReturn(identities)
instance.setIdentityApi(identityApi)
val settingsMap = hashMapOf(
"placementAttributesMapping" to """
[

]
""".trimIndent()
)
val field = KitConfiguration::class.java.getDeclaredField("settings")
field.isAccessible = true
field.set(mockedKitConfig, settingsMap)

val options = MParticleOptions.builder(MockContext())
.sideloadedKits(mutableListOf(sideloadedKit) as List<SideloadedKit>).build()
val manager: KitManagerImpl = MockKitManagerImpl(options)
val method: Method = KitManagerImpl::class.java.getDeclaredMethod(
"confirmEmail",
String::class.java,
MParticleUser::class.java,
IdentityApi::class.java,
Runnable::class.java
)
method.isAccessible = true
val result = method.invoke(manager, "Test@gmail.com", user, identityApi, runnable)
Mockito.verify(runnable).run()
}

@Test
fun testConfirmEmail_When_mailIsNull() {
var runnable: Runnable = Mockito.mock(Runnable::class.java)
var user: MParticleUser = Mockito.mock(MParticleUser::class.java)
val instance = MockMParticle()
val sideloadedKit = Mockito.mock(MPSideloadedKit::class.java)
val kitId = 6000000

val configJSONObj = JSONObject().apply {
put("id", kitId)
}
val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj)
Mockito.`when`(sideloadedKit.configuration).thenReturn(mockedKitConfig)
val identityApi = mock(IdentityApi::class.java)
val oldEmail = "Test@gmail.com"
val mockTask = mock(MParticleTask::class.java) as MParticleTask<IdentityApiResult>
`when`(identityApi.identify(any())).thenReturn(mockTask)
val identities: MutableMap<MParticle.IdentityType, String> = HashMap()
identities.put(MParticle.IdentityType.Email, oldEmail)
`when`(user.userIdentities).thenReturn(identities)
instance.setIdentityApi(identityApi)
val settingsMap = hashMapOf(
"placementAttributesMapping" to """
[

]
""".trimIndent()
)
val field = KitConfiguration::class.java.getDeclaredField("settings")
field.isAccessible = true
field.set(mockedKitConfig, settingsMap)

val options = MParticleOptions.builder(MockContext())
.sideloadedKits(mutableListOf(sideloadedKit) as List<SideloadedKit>).build()
val manager: KitManagerImpl = MockKitManagerImpl(options)
val method: Method = KitManagerImpl::class.java.getDeclaredMethod(
"confirmEmail",
String::class.java,
MParticleUser::class.java,
IdentityApi::class.java,
Runnable::class.java
)
method.isAccessible = true
val result = method.invoke(manager, null, user, identityApi, runnable)
Mockito.verify(runnable).run()
}

@Test
fun testConfirmEmail_When_User_IsNull() {
var runnable: Runnable = Mockito.mock(Runnable::class.java)
var user: MParticleUser = Mockito.mock(MParticleUser::class.java)
val instance = MockMParticle()
val sideloadedKit = Mockito.mock(MPSideloadedKit::class.java)
val kitId = 6000000

val configJSONObj = JSONObject().apply {
put("id", kitId)
}
val mockedKitConfig = KitConfiguration.createKitConfiguration(configJSONObj)
Mockito.`when`(sideloadedKit.configuration).thenReturn(mockedKitConfig)
val identityApi = mock(IdentityApi::class.java)
val oldEmail = "Test@gmail.com"
val mockTask = mock(MParticleTask::class.java) as MParticleTask<IdentityApiResult>
`when`(identityApi.identify(any())).thenReturn(mockTask)
val identities: MutableMap<MParticle.IdentityType, String> = HashMap()
identities.put(MParticle.IdentityType.Email, oldEmail)
`when`(user.userIdentities).thenReturn(identities)
instance.setIdentityApi(identityApi)
val settingsMap = hashMapOf(
"placementAttributesMapping" to """
[

]
""".trimIndent()
)
val field = KitConfiguration::class.java.getDeclaredField("settings")
field.isAccessible = true
field.set(mockedKitConfig, settingsMap)

val options = MParticleOptions.builder(MockContext())
.sideloadedKits(mutableListOf(sideloadedKit) as List<SideloadedKit>).build()
val manager: KitManagerImpl = MockKitManagerImpl(options)
val method: Method = KitManagerImpl::class.java.getDeclaredMethod(
"confirmEmail",
String::class.java,
MParticleUser::class.java,
IdentityApi::class.java,
Runnable::class.java
)
method.isAccessible = true
val result = method.invoke(manager, null, user, identityApi, runnable)
Mockito.verify(runnable).run()
}

internal inner class mockProvider(val config: KitConfiguration) : KitIntegration(), KitIntegration.RoktListener {
override fun isDisabled(): Boolean = false
override fun getName(): String = "FakeProvider"
Expand Down
Loading