From ec8b549541d22722d41eaa414779ce66e49a460f Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Fri, 20 Sep 2024 09:27:52 -0500 Subject: [PATCH 01/13] wip --- android-core/build.gradle | 11 ++++ .../com/mparticle/internal/ConfigManager.java | 9 +++ .../com/mparticle/internal/UploadHandler.java | 5 +- .../database/services/MParticleDBManager.java | 10 ++-- .../database/services/UploadService.java | 6 +- .../tables/MParticleDatabaseHelper.java | 9 ++- .../internal/database/tables/UploadTable.java | 4 ++ .../com/mparticle/database/UploadSettings.kt | 59 +++++++++++++++++++ build.gradle | 2 + 9 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt diff --git a/android-core/build.gradle b/android-core/build.gradle index e4b7d1df9..b63170578 100644 --- a/android-core/build.gradle +++ b/android-core/build.gradle @@ -1,3 +1,11 @@ +//plugins { +//// id 'org.jetbrains.kotlin.plugin.serialization' version '2.0.20' +//// id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.0' +// id 'com.android.library' +// id 'kotlin-android' +//} +//apply from: '../scripts/maven.gradle' + ext { kitDescription = 'Core mParticle SDK supporting only server-side integrations.' } @@ -136,6 +144,9 @@ task generateSourcesJar(type: Jar) { } dependencies { +// compileOnly 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1' +// compileOnly 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0' + //noinspection GradleCompatible compileOnly 'com.google.firebase:firebase-messaging:[10.2.1, )' compileOnly 'com.android.installreferrer:installreferrer:[1.0, )' diff --git a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java index 143f2822d..802130270 100644 --- a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java +++ b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java @@ -4,6 +4,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.net.Network; import android.os.Build; import androidx.annotation.NonNull; @@ -15,6 +16,7 @@ import com.mparticle.MParticle; import com.mparticle.MParticleOptions; import com.mparticle.consent.ConsentState; +import com.mparticle.database.UploadSettings; import com.mparticle.identity.IdentityApi; import com.mparticle.internal.messages.BaseMPMessage; import com.mparticle.networking.NetworkOptions; @@ -566,6 +568,13 @@ public void setLogUnhandledExceptions(boolean log) { sPreferences.edit().putBoolean(Constants.PrefKeys.REPORT_UNCAUGHT_EXCEPTIONS, log).apply(); } + public UploadSettings getUploadSettings() { +// NetworkOptions networkOptions = getNetworkOptions(); +// networkOptions. + // TODO: BEN - finish this + return new UploadSettings(getApiKey(), getApiSecret(),"", false, "", false, false, new int[]{}); + } + public String getApiKey() { return sPreferences.getString(Constants.PrefKeys.API_KEY, null); } diff --git a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java index cd51e8dc9..1b3a01e86 100644 --- a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java +++ b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java @@ -10,6 +10,7 @@ import androidx.annotation.Nullable; import com.mparticle.MParticle; +import com.mparticle.database.UploadSettings; import com.mparticle.identity.AliasRequest; import com.mparticle.identity.AliasResponse; import com.mparticle.internal.database.services.MParticleDBManager; @@ -131,7 +132,7 @@ public void handleMessageImpl(Message msg) { if (isNetworkConnected) { if (uploadInterval > 0 || msg.arg1 == 1) { while (mParticleDBManager.hasMessagesForUpload()) { - prepareMessageUploads(); + prepareMessageUploads(mConfigManager.getUploadSettings()); } upload(); } @@ -159,7 +160,7 @@ public void handleMessageImpl(Message msg) { * - persist all of the resulting upload batch objects * - mark the messages as having been uploaded. */ - protected void prepareMessageUploads() throws Exception { + protected void prepareMessageUploads(UploadSettings uploadSettings) throws Exception { String currentSessionId = mAppStateManager.getSession().mSessionID; long remainingHeap = MPUtility.getRemainingHeapInBytes(); if (remainingHeap < Constants.LIMIT_MAX_UPLOAD_SIZE) { diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java b/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java index 5b62292b2..2196704b3 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java @@ -11,6 +11,7 @@ import com.mparticle.MParticle; import com.mparticle.MParticleOptions; +import com.mparticle.database.UploadSettings; import com.mparticle.identity.UserAttributeListenerWrapper; import com.mparticle.internal.BatchId; import com.mparticle.internal.ConfigManager; @@ -24,6 +25,7 @@ import com.mparticle.internal.MessageBatch; import com.mparticle.internal.MessageManager; import com.mparticle.internal.MessageManagerCallbacks; +import com.mparticle.internal.UploadHandler; import com.mparticle.internal.database.MPDatabase; import com.mparticle.internal.database.MPDatabaseImpl; import com.mparticle.internal.listeners.InternalListenerManager; @@ -141,7 +143,7 @@ public boolean hasMessagesForUpload() { return MessageService.hasMessagesForUpload(db); } - public void createMessagesForUploadMessage(ConfigManager configManager, DeviceAttributes deviceAttributes, String currentSessionId) throws JSONException { + public void createMessagesForUploadMessage(ConfigManager configManager, DeviceAttributes deviceAttributes, String currentSessionId, UploadSettings uploadSettings) throws JSONException { MPDatabase db = getDatabase(); db.beginTransaction(); try { @@ -186,7 +188,7 @@ public void createMessagesForUploadMessage(ConfigManager configManager, DeviceAt for (JSONObject deviceInfo : deviceInfos) { deviceAttributes.updateDeviceInfo(mContext, deviceInfo); } - createUploads(uploadMessagesByBatchId, db, deviceAttributes, configManager, currentSessionId); + createUploads(uploadMessagesByBatchId, db, deviceAttributes, configManager, currentSessionId, uploadSettings); db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -239,7 +241,7 @@ private HashMap getUploadMessageByBatchIdMap(List uploadMessagesByBatchId, MPDatabase db, DeviceAttributes deviceAttributes, ConfigManager configManager, String currentSessionId) { + private void createUploads(Map uploadMessagesByBatchId, MPDatabase db, DeviceAttributes deviceAttributes, ConfigManager configManager, String currentSessionId, UploadSettings uploadSettings) { for (Map.Entry messageBatchEntry : uploadMessagesByBatchId.entrySet()) { BatchId batchId = messageBatchEntry.getKey(); MessageBatch uploadMessage = messageBatchEntry.getValue(); @@ -274,7 +276,7 @@ private void createUploads(Map uploadMessagesByBatchId, M } } - UploadService.insertUpload(db, batch, configManager.getApiKey()); + UploadService.insertUpload(db, batch, uploadSettings); cleanSessions(currentSessionId); } } diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java b/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java index 122b88df9..b4b8ede2d 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java @@ -3,6 +3,7 @@ import android.content.ContentValues; import android.database.Cursor; +import com.mparticle.database.UploadSettings; import com.mparticle.internal.Constants; import com.mparticle.internal.database.MPDatabase; import com.mparticle.internal.database.tables.UploadTable; @@ -25,12 +26,13 @@ public static int cleanupUploadMessages(MPDatabase database) { * * @param message */ - public static void insertUpload(MPDatabase database, JSONObject message, String apiKey) { + public static void insertUpload(MPDatabase database, JSONObject message, UploadSettings uploadSettings) { ContentValues contentValues = new ContentValues(); - contentValues.put(UploadTableColumns.API_KEY, apiKey); + contentValues.put(UploadTableColumns.API_KEY, uploadSettings.getApiKey()); contentValues.put(UploadTableColumns.CREATED_AT, message.optLong(Constants.MessageKey.TIMESTAMP, System.currentTimeMillis())); contentValues.put(UploadTableColumns.MESSAGE, message.toString()); contentValues.put(UploadTableColumns.REQUEST_TYPE, UploadTable.UPLOAD_REQUEST); + contentValues.put(UploadTableColumns.UPLOAD_SETTINGS, uploadSettings.serialize()); InternalListenerManager.getListener().onCompositeObjects(message, contentValues); database.insert(UploadTableColumns.TABLE_NAME, null, contentValues); } diff --git a/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java b/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java index 7c1ff65c8..130658061 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java +++ b/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java @@ -20,7 +20,7 @@ public class MParticleDatabaseHelper implements SQLiteOpenHelperWrapper { private final Context mContext; - public static final int DB_VERSION = 9; + public static final int DB_VERSION = 10; private static String DB_NAME = "mparticle.db"; public static String getDbName() { @@ -72,6 +72,9 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < 9) { upgradeMessageTable(db); } + if (oldVersion < 10) { + upgradeUploadsTable(db); + } } catch (Exception e) { Logger.warning("Exception while upgrading SQLite Database:\n" + e.getMessage() + "\nThis may have been caused by the database having been already upgraded"); } @@ -158,4 +161,8 @@ private void removeGcmTable(SQLiteDatabase db) { e.printStackTrace(); } } + + private void upgradeUploadsTable(SQLiteDatabase db) { + db.execSQL(UploadTable.UPLOAD_ADD_UPLOAD_SETTINGS_COLUMN); + } } diff --git a/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java b/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java index 7778a8002..2e21b3b19 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java +++ b/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java @@ -18,6 +18,7 @@ protected interface UploadTableColumns extends BaseColumns { */ String REQUEST_TYPE = "cfuuid"; String SESSION_ID = "session_id"; + String UPLOAD_SETTINGS = "upload_settings"; } @@ -30,4 +31,7 @@ protected interface UploadTableColumns extends BaseColumns { UploadTableColumns.REQUEST_TYPE + " TEXT, " + UploadTableColumns.SESSION_ID + " TEXT" + ");"; + + static final String UPLOAD_ADD_UPLOAD_SETTINGS_COLUMN = "ALTER TABLE " + UploadTableColumns.TABLE_NAME + + " ADD COLUMN " + UploadTableColumns.UPLOAD_SETTINGS + " TEXT"; } diff --git a/android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt b/android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt new file mode 100644 index 000000000..6732f540d --- /dev/null +++ b/android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt @@ -0,0 +1,59 @@ +package com.mparticle.database + +//import kotlinx.serialization.Serializable +//import kotlinx.serialization.json.Json +//import kotlinx.serialization.encodeToString + +//@Serializable +data class UploadSettings( + val apiKey: String, + val secret: String, + val eventsHost: String, + val overridesEventsSubdirectory: Boolean, + val aliasHost: String, + val overridesAliasSubdirectory: Boolean, + val eventsOnly: Boolean, + val kitIds: IntArray +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as UploadSettings + + if (apiKey != other.apiKey) return false + if (secret != other.secret) return false + if (eventsHost != other.eventsHost) return false + if (overridesEventsSubdirectory != other.overridesEventsSubdirectory) return false + if (aliasHost != other.aliasHost) return false + if (overridesAliasSubdirectory != other.overridesAliasSubdirectory) return false + if (eventsOnly != other.eventsOnly) return false + if (!kitIds.contentEquals(other.kitIds)) return false + + return true + } + + override fun hashCode(): Int { + var result = apiKey.hashCode() + result = 31 * result + secret.hashCode() + result = 31 * result + eventsHost.hashCode() + result = 31 * result + overridesEventsSubdirectory.hashCode() + result = 31 * result + aliasHost.hashCode() + result = 31 * result + overridesAliasSubdirectory.hashCode() + result = 31 * result + eventsOnly.hashCode() + result = 31 * result + kitIds.contentHashCode() + return result + } + + fun serialize(): String { +// return Json.encodeToString(this) + return "" + } + +// companion object { +// @JvmStatic +// fun deserialize(jsonString: String): UploadSettings { +// return Json.decodeFromString(jsonString) +// } +// } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index af0f34519..b37835779 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,8 @@ buildscript { plugins { id "org.sonarqube" version "3.5.0.2730" id "org.jlleitschuh.gradle.ktlint" version "11.2.0" +// id 'org.jetbrains.kotlin.jvm' version '2.0.20' apply false +// id 'org.jetbrains.kotlin.jvm' version '1.8.0' apply false } sonarqube { From a0c2c638ff9baf840fef265488cba2dedaf04687 Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Wed, 25 Sep 2024 10:48:24 -0500 Subject: [PATCH 02/13] Added switch workspace method --- .../networking/MParticleBaseClientImplTest.kt | 4 +- .../main/java/com/mparticle/MParticle.java | 34 +++++++- .../identity/MParticleIdentityClientImpl.java | 2 +- .../com/mparticle/internal/ConfigManager.java | 38 +++++++-- .../internal/MParticleApiClient.java | 5 +- .../internal/MParticleApiClientImpl.java | 60 ++++----------- .../mparticle/internal/MessageHandler.java | 3 +- .../mparticle/internal/MessageManager.java | 10 +++ .../internal/MessageManagerCallbacks.java | 3 + .../com/mparticle/internal/UploadHandler.java | 18 ++--- .../internal/database/UploadSettings.java | 77 +++++++++++++++++++ .../database/services/BreadcrumbService.java | 4 + .../database/services/MParticleDBManager.java | 32 ++++++-- .../database/services/MessageService.java | 6 ++ .../database/services/ReportingService.java | 5 ++ .../services/SQLiteOpenHelperWrapper.java | 1 - .../database/services/SessionService.java | 5 ++ .../database/services/UploadService.java | 21 +++-- .../services/UserAttributesService.java | 5 ++ .../tables/MParticleDatabaseHelper.java | 1 + .../networking/MParticleBaseClientImpl.java | 20 +++-- .../mparticle/networking/NetworkOptions.java | 2 +- .../com/mparticle/database/UploadSettings.kt | 59 -------------- .../internal/MParticleApiClientImplTest.kt | 1 - .../com/mparticle/internal/AccessUtils.java | 6 +- 25 files changed, 262 insertions(+), 160 deletions(-) create mode 100644 android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java delete mode 100644 android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt index 61e8bd59b..ea1abb64c 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt @@ -217,11 +217,11 @@ class MParticleBaseClientImplTest : BaseCleanInstallEachTest() { MParticle.start(options) val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { - val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name, true) + val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name) assertEquals(defaultUrls[endpoint].toString(), generatedUrl.defaultUrl.toString()) } for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { - val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name, false) + val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name) Assert.assertNotEquals(defaultUrls[endpoint].toString(), generatedUrl.toString()) Assert.assertFalse(generatedUrl === generatedUrl.defaultUrl) assertEquals( diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index 7246d51ee..105fd4a27 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -261,6 +261,29 @@ public static void setInstance(@Nullable MParticle instance) { MParticle.instance = instance; } + public static void switchWorkspace(@NonNull MParticleOptions options) { + if (instance != null) { + // End session if active + if (instance.isSessionActive()) { + instance.endSession(); + } + + // Batch any remaining messages into upload records + try { + instance.mMessageManager.mUploadHandler.prepareMessageUploads(instance.mConfigManager.getUploadSettings()); + } catch (Exception e) { + Logger.error(e, "Unable to create upload records before switching workspaces"); + } + } + + // Reset everything except for uploads table + reset(options.getContext(), false, true); + + // Restart the SDK using new options + instance = null; + start(options); + } + /** * @return false if Android ID collection is enabled. (true by default) * @see MParticleOptions.Builder#androidIdEnabled(boolean) @@ -1152,10 +1175,14 @@ public IdentityApi Identity() { * @param context */ public static void reset(@NonNull Context context) { - reset(context, true); + reset(context, true, false); } - static void reset(@NonNull Context context, boolean deleteDatabase) { + static void resetForSwitchingWorkspaces(@NonNull Context context) { + reset(context, false, true); + } + + static void reset(@NonNull Context context, boolean deleteDatabase, boolean switchingWorkspaces) { synchronized (MParticle.class) { //"commit" will force all async writes stemming from an "apply" call to finish. We need to do this //because we need to ensure that the "getMpids()" call is returning all calls that have been made @@ -1200,8 +1227,11 @@ static void reset(@NonNull Context context, boolean deleteDatabase) { } } } + if (deleteDatabase) { context.deleteDatabase(MParticleDatabaseHelper.getDbName()); + } else if (switchingWorkspaces) { + new MParticleDBManager(context).resetDatabaseForWorkspaceSwitching(); } } } diff --git a/android-core/src/main/java/com/mparticle/identity/MParticleIdentityClientImpl.java b/android-core/src/main/java/com/mparticle/identity/MParticleIdentityClientImpl.java index 06117fe7f..015342204 100644 --- a/android-core/src/main/java/com/mparticle/identity/MParticleIdentityClientImpl.java +++ b/android-core/src/main/java/com/mparticle/identity/MParticleIdentityClientImpl.java @@ -297,7 +297,7 @@ MPUrl getUrl(long mpId, String endpoint) throws MalformedURLException { } MPUrl getUrl(String endpoint) throws MalformedURLException { - return getUrl(Endpoint.IDENTITY, endpoint); + return getUrl(Endpoint.IDENTITY, endpoint, null); } private String getApiKey() { diff --git a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java index 802130270..a8144aba3 100644 --- a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java +++ b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java @@ -4,7 +4,6 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.net.Network; import android.os.Build; import androidx.annotation.NonNull; @@ -16,8 +15,8 @@ import com.mparticle.MParticle; import com.mparticle.MParticleOptions; import com.mparticle.consent.ConsentState; -import com.mparticle.database.UploadSettings; import com.mparticle.identity.IdentityApi; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.messages.BaseMPMessage; import com.mparticle.networking.NetworkOptions; import com.mparticle.networking.NetworkOptionsManager; @@ -503,6 +502,26 @@ public String getActiveModuleIds() { } } + public String getSupportedKitString() { + MParticle instance = MParticle.getInstance(); + if (instance != null) { + Set supportedKitIds = instance.Internal().getKitManager().getSupportedKits(); + if (supportedKitIds != null && !supportedKitIds.isEmpty()) { + StringBuilder buffer = new StringBuilder(supportedKitIds.size() * 3); + Iterator it = supportedKitIds.iterator(); + while (it.hasNext()) { + Integer next = it.next(); + buffer.append(next); + if (it.hasNext()) { + buffer.append(","); + } + } + return buffer.toString(); + } + } + return ""; + } + /** * When the Config manager starts up, we don't want to enable everything immediately to save on app-load time. * This method will be called from a background thread after startup is already complete. @@ -568,11 +587,14 @@ public void setLogUnhandledExceptions(boolean log) { sPreferences.edit().putBoolean(Constants.PrefKeys.REPORT_UNCAUGHT_EXCEPTIONS, log).apply(); } + // TODO: BEN - return null instead of setting empty strings? public UploadSettings getUploadSettings() { -// NetworkOptions networkOptions = getNetworkOptions(); -// networkOptions. - // TODO: BEN - finish this - return new UploadSettings(getApiKey(), getApiSecret(),"", false, "", false, false, new int[]{}); + String apiKey = getApiKey(); + String secret = getApiSecret(); + return new UploadSettings( + apiKey == null ? "" : apiKey, + secret == null ? "" : secret, + getNetworkOptions(), getActiveModuleIds(), getSupportedKitString()); } public String getApiKey() { @@ -1294,10 +1316,10 @@ public boolean isDirectUrlRoutingEnabled() { silo and a hyphen (ex. "us1-", "us2-", "eu1-"). us1 was the first silo,and before other silos existed, there were no prefixes and all apiKeys were us1. As such, if we split on a '-' and the resulting array length is 1, then it is an older APIkey that should route to us1. When splitKey.length is greater than 1, then splitKey[0] will be us1, us2, eu1, au1, or st1, etc as new silos are added */ - public String getPodPrefix() { + public String getPodPrefix(@NonNull String apiKey) { String prefix = "us1"; try { - String[] prefixFromApi = getApiKey().split("-"); + String[] prefixFromApi = apiKey.split("-"); if (prefixFromApi.length > 1) { prefix = prefixFromApi[0]; } diff --git a/android-core/src/main/java/com/mparticle/internal/MParticleApiClient.java b/android-core/src/main/java/com/mparticle/internal/MParticleApiClient.java index 5d3593179..0299a26d6 100644 --- a/android-core/src/main/java/com/mparticle/internal/MParticleApiClient.java +++ b/android-core/src/main/java/com/mparticle/internal/MParticleApiClient.java @@ -3,6 +3,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.networking.MParticleBaseClient; import org.json.JSONException; @@ -18,14 +19,14 @@ public interface MParticleApiClient extends MParticleBaseClient { void fetchConfig(boolean force) throws IOException, MParticleApiClientImpl.MPConfigException; - int sendMessageBatch(String message) throws IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException; + int sendMessageBatch(@NonNull String message, @NonNull UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException; JSONObject fetchAudiences(); JSONObject getCookies(); @NonNull - AliasNetworkResponse sendAliasRequest(@NonNull String message) throws JSONException, IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException; + AliasNetworkResponse sendAliasRequest(@NonNull String message, @NonNull UploadSettings uploadSettings) throws JSONException, IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException; class AliasNetworkResponse { private int responseCode; diff --git a/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java b/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java index 12b828667..2df09756e 100644 --- a/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java +++ b/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java @@ -3,8 +3,11 @@ import android.content.Context; import android.content.SharedPreferences; +import androidx.annotation.NonNull; + import com.mparticle.MParticle; import com.mparticle.SdkListener; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.listeners.InternalListenerManager; import com.mparticle.networking.MPConnection; import com.mparticle.networking.MPUrl; @@ -61,14 +64,11 @@ public class MParticleApiClientImpl extends MParticleBaseClientImpl implements M private final ConfigManager mConfigManager; private final String mApiSecret; private MPUrl mConfigUrl; - private MPUrl mEventUrl; - private MPUrl mAliasUrl; private final String mUserAgent; private final SharedPreferences mPreferences; private final String mApiKey; private final Context mContext; Integer mDeviceRampNumber = null; - private static String sSupportedKits; private JSONObject mCurrentCookies; /** @@ -104,11 +104,6 @@ void setConfigUrl(MPUrl configUrl) { mConfigUrl = configUrl; } - static void setSupportedKitString(String supportedKitString) { - sSupportedKits = supportedKitString; - } - - @Override public void fetchConfig() throws IOException, MPConfigException { fetchConfig(false); @@ -139,7 +134,7 @@ public void fetchConfig(boolean force) throws IOException, MPConfigException { connection.setReadTimeout(mConfigManager.getConnectionTimeout()); connection.setRequestProperty(HEADER_ENVIRONMENT, Integer.toString(mConfigManager.getEnvironment().getValue())); - String supportedKits = getSupportedKitString(); + String supportedKits = mConfigManager.getSupportedKitString(); if (!MPUtility.isEmpty(supportedKits)) { connection.setRequestProperty(HEADER_KITS, supportedKits); } @@ -227,13 +222,11 @@ public JSONObject fetchAudiences() { return response; } - public int sendMessageBatch(String message) throws IOException, MPThrottleException, MPRampException { + public int sendMessageBatch(@NonNull String message, @NonNull UploadSettings uploadSettings) throws IOException, MPThrottleException, MPRampException { checkThrottleTime(Endpoint.EVENTS); checkRampValue(); - if (mEventUrl == null) { - mEventUrl = getUrl(Endpoint.EVENTS); - } - MPConnection connection = mEventUrl.openConnection(); + MPUrl eventUrl = getUrl(Endpoint.EVENTS,null, uploadSettings); + MPConnection connection = eventUrl.openConnection(); connection.setConnectTimeout(mConfigManager.getConnectionTimeout()); connection.setReadTimeout(mConfigManager.getConnectionTimeout()); connection.setDoOutput(true); @@ -242,11 +235,11 @@ public int sendMessageBatch(String message) throws IOException, MPThrottleExcept connection.setRequestProperty("Content-Encoding", "gzip"); connection.setRequestProperty("User-Agent", mUserAgent); - String activeKits = mConfigManager.getActiveModuleIds(); + String activeKits = uploadSettings.getActiveKits(); if (!MPUtility.isEmpty(activeKits)) { connection.setRequestProperty(HEADER_KITS, activeKits); } - String supportedKits = getSupportedKitString(); + String supportedKits = uploadSettings.getSupportedKits(); if (!MPUtility.isEmpty(supportedKits)) { connection.setRequestProperty(HEADER_BUNDLED_KITS, supportedKits); } @@ -263,7 +256,7 @@ public int sendMessageBatch(String message) throws IOException, MPThrottleExcept makeUrlRequest(Endpoint.EVENTS, connection, message, true); Logger.verbose("Upload request attempt:\n" + - "URL- " + mEventUrl.toString()); + "URL- " + eventUrl.toString()); Logger.verbose(message); @@ -290,15 +283,14 @@ public int sendMessageBatch(String message) throws IOException, MPThrottleExcept return connection.getResponseCode(); } + @NonNull @Override - public AliasNetworkResponse sendAliasRequest(String message) throws IOException, MPThrottleException, MPRampException { + public AliasNetworkResponse sendAliasRequest(@NonNull String message, @NonNull UploadSettings uploadSettings) throws IOException, MPThrottleException, MPRampException { checkThrottleTime(Endpoint.ALIAS); Logger.verbose("Identity alias request:\n" + message); - if (mAliasUrl == null) { - mAliasUrl = getUrl(Endpoint.ALIAS); - } - MPConnection connection = mAliasUrl.openConnection(); + MPUrl aliasUrl = getUrl(Endpoint.ALIAS, null, uploadSettings); + MPConnection connection = aliasUrl.openConnection(); connection.setConnectTimeout(mConfigManager.getConnectionTimeout()); connection.setReadTimeout(mConfigManager.getConnectionTimeout()); connection.setDoOutput(true); @@ -416,30 +408,6 @@ private void checkRampValue() throws MPRampException { } } - private String getSupportedKitString() { - if (sSupportedKits == null) { - MParticle instance = MParticle.getInstance(); - if (instance != null) { - Set supportedKitIds = instance.Internal().getKitManager().getSupportedKits(); - if (supportedKitIds != null && supportedKitIds.size() > 0) { - StringBuilder buffer = new StringBuilder(supportedKitIds.size() * 3); - Iterator it = supportedKitIds.iterator(); - while (it.hasNext()) { - Integer next = it.next(); - buffer.append(next); - if (it.hasNext()) { - buffer.append(","); - } - } - sSupportedKits = buffer.toString(); - } - } else { - sSupportedKits = ""; - } - } - return sSupportedKits; - } - public void setCookies(JSONObject serverCookies) { if (serverCookies != null) { try { diff --git a/android-core/src/main/java/com/mparticle/internal/MessageHandler.java b/android-core/src/main/java/com/mparticle/internal/MessageHandler.java index f9ff968cd..6fb47bbf6 100644 --- a/android-core/src/main/java/com/mparticle/internal/MessageHandler.java +++ b/android-core/src/main/java/com/mparticle/internal/MessageHandler.java @@ -1,6 +1,7 @@ package com.mparticle.internal; import android.content.Context; +import android.graphics.Bitmap; import android.os.Looper; import android.os.Message; @@ -241,7 +242,7 @@ public void handleMessageImpl(Message msg) { case STORE_ALIAS_MESSAGE: try { MPAliasMessage aliasMessage = (MPAliasMessage) msg.obj; - mMParticleDBManager.insertAliasRequest(mMessageManagerCallbacks.getApiKey(), aliasMessage); + mMParticleDBManager.insertAliasRequest(aliasMessage, mMessageManagerCallbacks.getUploadSettings()); MParticle instance = MParticle.getInstance(); if (instance != null) { diff --git a/android-core/src/main/java/com/mparticle/internal/MessageManager.java b/android-core/src/main/java/com/mparticle/internal/MessageManager.java index 8824a7e2e..cd3fbf4af 100644 --- a/android-core/src/main/java/com/mparticle/internal/MessageManager.java +++ b/android-core/src/main/java/com/mparticle/internal/MessageManager.java @@ -29,6 +29,7 @@ import com.mparticle.identity.UserAttributeListenerWrapper; import com.mparticle.internal.Constants.MessageKey; import com.mparticle.internal.Constants.MessageType; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.database.services.MParticleDBManager; import com.mparticle.internal.messages.BaseMPMessage; import com.mparticle.internal.messages.BaseMPMessageBuilder; @@ -768,6 +769,15 @@ public String getApiKey() throws MParticleApiClientImpl.MPNoConfigException { return apiKey; } + @Override + public UploadSettings getUploadSettings() throws MParticleApiClientImpl.MPNoConfigException { + UploadSettings uploadSettings = mConfigManager.getUploadSettings(); + if (MPUtility.isEmpty(uploadSettings.getApiKey()) || MPUtility.isEmpty(uploadSettings.getSecret())) { + throw new MParticleApiClientImpl.MPNoConfigException(); + } + return uploadSettings; + } + @SuppressLint("MissingPermission") @Override public void delayedStart() { diff --git a/android-core/src/main/java/com/mparticle/internal/MessageManagerCallbacks.java b/android-core/src/main/java/com/mparticle/internal/MessageManagerCallbacks.java index 8e07baa5f..fe63b72a4 100644 --- a/android-core/src/main/java/com/mparticle/internal/MessageManagerCallbacks.java +++ b/android-core/src/main/java/com/mparticle/internal/MessageManagerCallbacks.java @@ -1,10 +1,13 @@ package com.mparticle.internal; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.messages.BaseMPMessage; public interface MessageManagerCallbacks { String getApiKey() throws MParticleApiClientImpl.MPNoConfigException; + UploadSettings getUploadSettings() throws MParticleApiClientImpl.MPNoConfigException; + void delayedStart(); void endUploadLoop(); diff --git a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java index 1b3a01e86..4b8ea8f64 100644 --- a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java +++ b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java @@ -10,9 +10,9 @@ import androidx.annotation.Nullable; import com.mparticle.MParticle; -import com.mparticle.database.UploadSettings; import com.mparticle.identity.AliasRequest; import com.mparticle.identity.AliasResponse; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.database.services.MParticleDBManager; import com.mparticle.internal.listeners.InternalListenerManager; import com.mparticle.internal.messages.MPAliasMessage; @@ -160,7 +160,7 @@ public void handleMessageImpl(Message msg) { * - persist all of the resulting upload batch objects * - mark the messages as having been uploaded. */ - protected void prepareMessageUploads(UploadSettings uploadSettings) throws Exception { + public void prepareMessageUploads(UploadSettings uploadSettings) throws Exception { String currentSessionId = mAppStateManager.getSession().mSessionID; long remainingHeap = MPUtility.getRemainingHeapInBytes(); if (remainingHeap < Constants.LIMIT_MAX_UPLOAD_SIZE) { @@ -173,7 +173,7 @@ protected void prepareMessageUploads(UploadSettings uploadSettings) throws Excep } try { mParticleDBManager.cleanupMessages(); - mParticleDBManager.createMessagesForUploadMessage(mConfigManager, mMessageManager.getDeviceAttributes(), currentSessionId); + mParticleDBManager.createMessagesForUploadMessage(mConfigManager, mMessageManager.getDeviceAttributes(), currentSessionId, uploadSettings); } catch (Exception e) { Logger.verbose("Error preparing batch upload in mParticle DB: " + e.getMessage()); } @@ -195,9 +195,9 @@ protected void upload() { String message = readyUpload.getMessage(); InternalListenerManager.getListener().onCompositeObjects(readyUpload, message); if (readyUpload.isAliasRequest()) { - uploadAliasRequest(readyUpload.getId(), message); + uploadAliasRequest(readyUpload.getId(), message, readyUpload.getUploadSettings()); } else { - uploadMessage(readyUpload.getId(), message); + uploadMessage(readyUpload.getId(), message, readyUpload.getUploadSettings()); } } } catch (MParticleApiClientImpl.MPThrottleException e) { @@ -210,11 +210,11 @@ protected void upload() { } } - void uploadMessage(int id, String message) throws IOException, MParticleApiClientImpl.MPThrottleException { + void uploadMessage(int id, String message, UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException { int responseCode = -1; boolean sampling = false; try { - responseCode = mApiClient.sendMessageBatch(message); + responseCode = mApiClient.sendMessageBatch(message, uploadSettings); } catch (MParticleApiClientImpl.MPRampException e) { sampling = true; Logger.debug("This device is being sampled."); @@ -234,12 +234,12 @@ void uploadMessage(int id, String message) throws IOException, MParticleApiClien } } - void uploadAliasRequest(int id, String aliasRequestMessage) throws IOException, MParticleApiClientImpl.MPThrottleException { + void uploadAliasRequest(int id, String aliasRequestMessage, UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException { MParticleApiClient.AliasNetworkResponse response = new MParticleApiClientImpl.AliasNetworkResponse(-1); boolean sampling = false; try { - response = mApiClient.sendAliasRequest(aliasRequestMessage); + response = mApiClient.sendAliasRequest(aliasRequestMessage, uploadSettings); } catch (JSONException e) { response.setErrorMessage("Unable to deserialize Alias Request"); Logger.error(response.getErrorMessage()); diff --git a/android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java b/android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java new file mode 100644 index 000000000..eadb39353 --- /dev/null +++ b/android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java @@ -0,0 +1,77 @@ +package com.mparticle.internal.database; +import androidx.annotation.NonNull; + +import com.mparticle.internal.Logger; +import com.mparticle.networking.NetworkOptions; + +import org.json.JSONException; +import org.json.JSONObject; + +public class UploadSettings { + private final String mApiKey; + private final String mSecret; + private final NetworkOptions mNetworkOptions; + private final String mActiveKits; + private final String mSupportedKits; + + // TODO: BEN - Accept null values? + public UploadSettings(@NonNull String apiKey, @NonNull String secret, @NonNull NetworkOptions networkOptions, @NonNull String activeKits, @NonNull String supportedKits) { + mApiKey = apiKey; + mSecret = secret; + mNetworkOptions = networkOptions; + mActiveKits = activeKits; + mSupportedKits = supportedKits; + } + + public String getApiKey() { + return mApiKey; + } + + public String getSecret() { + return mSecret; + } + + public NetworkOptions getNetworkOptions() { + return mNetworkOptions; + } + + public String getActiveKits() { + return mActiveKits; + } + + public String getSupportedKits() { + return mSupportedKits; + } + + public String toJson() { + JSONObject uploadSettingsJson = new JSONObject(); + try { + uploadSettingsJson.put("apiKey", mApiKey); + uploadSettingsJson.put("secret", mSecret); + uploadSettingsJson.put("networkOptions", mNetworkOptions.toJson()); + uploadSettingsJson.put("activeKits", mActiveKits); + uploadSettingsJson.put("supportedKits", mSupportedKits); + } catch (JSONException jse) { + Logger.error(jse); + } + return uploadSettingsJson.toString(); + } + + public static UploadSettings withJson(String jsonString) { + try { + JSONObject jsonObject = new JSONObject(jsonString); + String apiKey = jsonObject.getString("apiKey"); + String secret = jsonObject.getString("secret"); + JSONObject networkOptionsJsonObject = jsonObject.getJSONObject("networkOptions"); + NetworkOptions networkOptions = NetworkOptions.withNetworkOptions(networkOptionsJsonObject.toString()); + String activeKits = jsonObject.getString("activeKits"); + String supportedKits = jsonObject.getString("supportedKits"); + if (networkOptions != null) { + return new UploadSettings(apiKey, secret, networkOptions, activeKits, supportedKits); + } + } catch (JSONException jse) { + Logger.error(jse); + } + return null; + } +} diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/BreadcrumbService.java b/android-core/src/main/java/com/mparticle/internal/database/services/BreadcrumbService.java index e4cb60184..ab3f827c1 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/BreadcrumbService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/BreadcrumbService.java @@ -105,4 +105,8 @@ public static JSONArray getBreadcrumbs(MPDatabase db, Context context, Long mpid } return new JSONArray(); } + + public static void deleteAll(MPDatabase db) { + db.delete(BreadcrumbTableColumns.TABLE_NAME, null, null); + } } diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java b/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java index 2196704b3..9e9015946 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java @@ -11,7 +11,6 @@ import com.mparticle.MParticle; import com.mparticle.MParticleOptions; -import com.mparticle.database.UploadSettings; import com.mparticle.identity.UserAttributeListenerWrapper; import com.mparticle.internal.BatchId; import com.mparticle.internal.ConfigManager; @@ -25,9 +24,9 @@ import com.mparticle.internal.MessageBatch; import com.mparticle.internal.MessageManager; import com.mparticle.internal.MessageManagerCallbacks; -import com.mparticle.internal.UploadHandler; import com.mparticle.internal.database.MPDatabase; import com.mparticle.internal.database.MPDatabaseImpl; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.listeners.InternalListenerManager; import com.mparticle.internal.messages.BaseMPMessage; @@ -476,8 +475,8 @@ public int deleteUpload(int id) { return UploadService.deleteUpload(getDatabase(), id); } - public void insertAliasRequest(String apiKey, JSONObject request) { - UploadService.insertAliasRequest(getDatabase(), apiKey, request); + public void insertAliasRequest(JSONObject request, UploadSettings uploadSettings) { + UploadService.insertAliasRequest(getDatabase(), request, uploadSettings); } /** @@ -646,6 +645,22 @@ public void removeUserAttribute(UserAttributeRemoval container, MessageManagerCa } } + public void resetDatabaseForWorkspaceSwitching() { + MPDatabase db = getDatabase(); + try { + db.beginTransaction(); + BreadcrumbService.deleteAll(db); + MessageService.deleteAll(db); + ReportingService.deleteAll(db); + SessionService.deleteAll(db); + UserAttributesService.deleteAll(db); + db.setTransactionSuccessful(); + } catch (Exception e) { + } finally { + db.endTransaction(); + } + } + public static class AttributionChange { private String key; private Object newValue; @@ -698,14 +713,15 @@ public static class ReadyUpload { private int id; private String message; private boolean isAliasRequest; + private UploadSettings uploadSettings; - public ReadyUpload(int id, boolean isAliasRequest, String message) { + public ReadyUpload(int id, boolean isAliasRequest, String message, UploadSettings uploadSettings) { this.id = id; this.message = message; this.isAliasRequest = isAliasRequest; + this.uploadSettings = uploadSettings; } - public int getId() { return id; } @@ -717,6 +733,10 @@ public String getMessage() { public boolean isAliasRequest() { return isAliasRequest; } + + public UploadSettings getUploadSettings() { + return uploadSettings; + } } public static class UserAttributeRemoval { diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java b/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java index 8bc11d9e9..135352ef8 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java @@ -2,10 +2,12 @@ import android.content.ContentValues; import android.database.Cursor; +import android.os.Message; import com.mparticle.internal.Constants; import com.mparticle.internal.Logger; import com.mparticle.internal.database.MPDatabase; +import com.mparticle.internal.database.tables.BreadcrumbTable; import com.mparticle.internal.database.tables.MessageTable; import com.mparticle.internal.listeners.InternalListenerManager; import com.mparticle.internal.messages.BaseMPMessage; @@ -231,6 +233,10 @@ public static void insertMessage(MPDatabase db, String apiKey, BaseMPMessage mes db.insert(MessageTableColumns.TABLE_NAME, null, contentValues); } + public static void deleteAll(MPDatabase db) { + db.delete(MessageTableColumns.TABLE_NAME, null, null); + } + public static class ReadyMessage { private long mpid; private String sessionId; diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/ReportingService.java b/android-core/src/main/java/com/mparticle/internal/database/services/ReportingService.java index 3ffa3b7eb..3fe3ec7dc 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/ReportingService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/ReportingService.java @@ -6,6 +6,7 @@ import com.mparticle.internal.Constants; import com.mparticle.internal.JsonReportingMessage; import com.mparticle.internal.database.MPDatabase; +import com.mparticle.internal.database.tables.BreadcrumbTable; import com.mparticle.internal.database.tables.ReportingTable; import com.mparticle.internal.listeners.InternalListenerManager; @@ -80,6 +81,10 @@ public static void deleteReportingMessage(MPDatabase database, int messageId) { database.delete(ReportingTableColumns.TABLE_NAME, whereClause, whereArgs); } + public static void deleteAll(MPDatabase db) { + db.delete(ReportingTableColumns.TABLE_NAME, null, null); + } + public static class ReportingMessage { private long mpid; private JSONObject msgObject; diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/SQLiteOpenHelperWrapper.java b/android-core/src/main/java/com/mparticle/internal/database/services/SQLiteOpenHelperWrapper.java index 55b7a1e2e..ecf5cac0a 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/SQLiteOpenHelperWrapper.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/SQLiteOpenHelperWrapper.java @@ -9,5 +9,4 @@ public interface SQLiteOpenHelperWrapper { void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion); void onDowngrade(SQLiteDatabase database, int oldVersion, int newVersion); - } diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java b/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java index 8560e903e..142cf11fb 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java @@ -12,6 +12,7 @@ import com.mparticle.internal.Constants; import com.mparticle.internal.MessageBatch; import com.mparticle.internal.database.MPDatabase; +import com.mparticle.internal.database.tables.BreadcrumbTable; import com.mparticle.internal.database.tables.SessionTable; import com.mparticle.internal.messages.BaseMPMessage; @@ -180,4 +181,8 @@ static Map> flattenBySessionId(Map getReadyUploads(MPDatabase da List readyUploads = new ArrayList(); Cursor readyUploadsCursor = null; try { - readyUploadsCursor = database.query(UploadTableColumns.TABLE_NAME, new String[]{"_id", UploadTableColumns.MESSAGE, UploadTableColumns.REQUEST_TYPE}, + readyUploadsCursor = database.query(UploadTableColumns.TABLE_NAME, new String[]{"_id", UploadTableColumns.MESSAGE, UploadTableColumns.REQUEST_TYPE, UploadTableColumns.UPLOAD_SETTINGS}, null, null, null, null, UploadTableColumns.CREATED_AT); int messageIdIndex = readyUploadsCursor.getColumnIndexOrThrow(UploadTableColumns._ID); int messageIndex = readyUploadsCursor.getColumnIndexOrThrow(UploadTableColumns.MESSAGE); int requestTypeIndex = readyUploadsCursor.getColumnIndexOrThrow(UploadTableColumns.REQUEST_TYPE); + int uploadSettingsIndex = readyUploadsCursor.getColumnIndexOrThrow(UploadTableColumns.UPLOAD_SETTINGS); while (readyUploadsCursor.moveToNext()) { - MParticleDBManager.ReadyUpload readyUpload = new MParticleDBManager.ReadyUpload(readyUploadsCursor.getInt(messageIdIndex), UploadTable.ALIAS_REQUEST.equals(readyUploadsCursor.getString(requestTypeIndex)), readyUploadsCursor.getString(messageIndex)); + MParticleDBManager.ReadyUpload readyUpload = new MParticleDBManager.ReadyUpload(readyUploadsCursor.getInt(messageIdIndex), UploadTable.ALIAS_REQUEST.equals(readyUploadsCursor.getString(requestTypeIndex)), readyUploadsCursor.getString(messageIndex), UploadSettings.withJson(readyUploadsCursor.getString(uploadSettingsIndex))); readyUploads.add(readyUpload); InternalListenerManager.getListener().onCompositeObjects(readyUploadsCursor, readyUpload); } + } catch (Exception e) { + Logger.error(e, "Failed to get ready uploads"); } finally { if (readyUploadsCursor != null && !readyUploadsCursor.isClosed()) { readyUploadsCursor.close(); } - return readyUploads; } - + return readyUploads; } /** @@ -71,12 +75,13 @@ public static int deleteUpload(MPDatabase database, int id) { return database.delete(UploadTableColumns.TABLE_NAME, "_id=?", whereArgs); } - public static long insertAliasRequest(MPDatabase database, String apiKey, JSONObject request) { + public static long insertAliasRequest(MPDatabase database, JSONObject request, UploadSettings uploadSettings) { ContentValues contentValues = new ContentValues(); - contentValues.put(UploadTableColumns.API_KEY, apiKey); + contentValues.put(UploadTableColumns.API_KEY, uploadSettings.getApiKey()); contentValues.put(UploadTableColumns.CREATED_AT, System.currentTimeMillis()); contentValues.put(UploadTableColumns.MESSAGE, request.toString()); contentValues.put(UploadTableColumns.REQUEST_TYPE, UploadTable.ALIAS_REQUEST); + contentValues.put(UploadTableColumns.UPLOAD_SETTINGS, uploadSettings.toJson()); InternalListenerManager.getListener().onCompositeObjects(request, contentValues); return database.insert(UploadTableColumns.TABLE_NAME, null, contentValues); } diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/UserAttributesService.java b/android-core/src/main/java/com/mparticle/internal/database/services/UserAttributesService.java index 17d695172..1cdb6743d 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/UserAttributesService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/UserAttributesService.java @@ -5,6 +5,7 @@ import com.mparticle.internal.Logger; import com.mparticle.internal.database.MPDatabase; +import com.mparticle.internal.database.tables.BreadcrumbTable; import com.mparticle.internal.database.tables.UserAttributesTable; import java.util.ArrayList; @@ -78,4 +79,8 @@ public static TreeMap> getUserAttributesLists(MPDatabase db } return attributes; } + + public static void deleteAll(MPDatabase db) { + db.delete(UserAttributesTableColumns.TABLE_NAME, null, null); + } } diff --git a/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java b/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java index 130658061..37965343b 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java +++ b/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java @@ -164,5 +164,6 @@ private void removeGcmTable(SQLiteDatabase db) { private void upgradeUploadsTable(SQLiteDatabase db) { db.execSQL(UploadTable.UPLOAD_ADD_UPLOAD_SETTINGS_COLUMN); + // TODO: BEN - insert current upload settings } } diff --git a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java index eb7a0cce4..77abff95f 100644 --- a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java +++ b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java @@ -11,6 +11,7 @@ import com.mparticle.internal.Constants; import com.mparticle.internal.Logger; import com.mparticle.internal.MPUtility; +import com.mparticle.internal.database.UploadSettings; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -92,19 +93,16 @@ public long getNextRequestTime(Endpoint endpoint) { } protected MPUrl getUrl(Endpoint endpoint) throws MalformedURLException { - return getUrl(endpoint, null); + return getUrl(endpoint, null, null); } - protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath) throws MalformedURLException { - return getUrl(endpoint, identityPath, false); - } - - protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, boolean forceDefaultUrl) throws MalformedURLException { - NetworkOptions networkOptions = mConfigManager.getNetworkOptions(); + protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, @Nullable UploadSettings uploadSettings) throws MalformedURLException { + NetworkOptions networkOptions = uploadSettings == null ? mConfigManager.getNetworkOptions() : uploadSettings.getNetworkOptions(); DomainMapping domainMapping = networkOptions.getDomain(endpoint); String url = NetworkOptionsManager.getDefaultUrl(endpoint); + String apiKey = uploadSettings == null ? mApiKey : uploadSettings.getApiKey(); - // `defaultDomain` variable is for URL generation when domain mapping is specified. + // `defaultDomain` variable is for URL generation when domain mapping is specified. String defaultDomain = url; boolean isDefaultDomain = true; @@ -118,7 +116,7 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, boolean if (endpoint != Endpoint.CONFIG) { // Set URL with pod prefix if it’s the default domain and endpoint is not CONFIG if (isDefaultDomain) { - url = getPodUrl(url, mConfigManager.getPodPrefix(), mConfigManager.isDirectUrlRoutingEnabled()); + url = getPodUrl(url, mConfigManager.getPodPrefix(apiKey), mConfigManager.isDirectUrlRoutingEnabled()); } else { // When domain mapping is specified, generate the default domain. Whether podRedirection is enabled or not, always use the original URL. defaultDomain = getPodUrl(defaultDomain, null, false); @@ -156,7 +154,7 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, boolean case EVENTS: pathPrefix = SERVICE_VERSION_2 + "/"; subdirectory = overridesSubdirectory ? "" : pathPrefix; - pathPostfix = mApiKey + "/events"; + pathPostfix = apiKey + "/events"; uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) @@ -167,7 +165,7 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, boolean case ALIAS: pathPrefix = SERVICE_VERSION_1 + "/identity/"; subdirectory = overridesSubdirectory ? "" : pathPrefix; - pathPostfix = mApiKey + "/alias"; + pathPostfix = apiKey + "/alias"; uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) diff --git a/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java b/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java index b0ff76b8f..195f95a68 100644 --- a/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java +++ b/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java @@ -114,7 +114,7 @@ public String toString() { } - private JSONObject toJson() { + public JSONObject toJson() { JSONObject networkOptions = new JSONObject(); try { JSONArray domainMappingsJson = new JSONArray(); diff --git a/android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt b/android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt deleted file mode 100644 index 6732f540d..000000000 --- a/android-core/src/main/kotlin/com/mparticle/database/UploadSettings.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.mparticle.database - -//import kotlinx.serialization.Serializable -//import kotlinx.serialization.json.Json -//import kotlinx.serialization.encodeToString - -//@Serializable -data class UploadSettings( - val apiKey: String, - val secret: String, - val eventsHost: String, - val overridesEventsSubdirectory: Boolean, - val aliasHost: String, - val overridesAliasSubdirectory: Boolean, - val eventsOnly: Boolean, - val kitIds: IntArray -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as UploadSettings - - if (apiKey != other.apiKey) return false - if (secret != other.secret) return false - if (eventsHost != other.eventsHost) return false - if (overridesEventsSubdirectory != other.overridesEventsSubdirectory) return false - if (aliasHost != other.aliasHost) return false - if (overridesAliasSubdirectory != other.overridesAliasSubdirectory) return false - if (eventsOnly != other.eventsOnly) return false - if (!kitIds.contentEquals(other.kitIds)) return false - - return true - } - - override fun hashCode(): Int { - var result = apiKey.hashCode() - result = 31 * result + secret.hashCode() - result = 31 * result + eventsHost.hashCode() - result = 31 * result + overridesEventsSubdirectory.hashCode() - result = 31 * result + aliasHost.hashCode() - result = 31 * result + overridesAliasSubdirectory.hashCode() - result = 31 * result + eventsOnly.hashCode() - result = 31 * result + kitIds.contentHashCode() - return result - } - - fun serialize(): String { -// return Json.encodeToString(this) - return "" - } - -// companion object { -// @JvmStatic -// fun deserialize(jsonString: String): UploadSettings { -// return Json.decodeFromString(jsonString) -// } -// } -} \ No newline at end of file diff --git a/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt index 5d6230d65..bad34528d 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt @@ -40,7 +40,6 @@ class MParticleApiClientImplTest { MockContext() ) client.mDeviceRampNumber = 50 - MParticleApiClientImpl.setSupportedKitString("") val mockUrl = PowerMockito.mock(MPUrl::class.java) mockConnection = PowerMockito.mock(MPConnection::class.java) Mockito.`when`(mockUrl.openConnection()).thenReturn(mockConnection) diff --git a/testutils/src/main/java/com/mparticle/internal/AccessUtils.java b/testutils/src/main/java/com/mparticle/internal/AccessUtils.java index 178378f2c..5e6502be8 100644 --- a/testutils/src/main/java/com/mparticle/internal/AccessUtils.java +++ b/testutils/src/main/java/com/mparticle/internal/AccessUtils.java @@ -8,6 +8,7 @@ import com.mparticle.MParticle; import com.mparticle.identity.IdentityStateListener; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.database.tables.MParticleDatabaseHelper; import com.mparticle.kits.KitManagerImpl; import com.mparticle.networking.BaseNetworkConnection; @@ -111,7 +112,7 @@ public void fetchConfig(boolean force) throws IOException, MParticleApiClientImp } @Override - public int sendMessageBatch(String message) throws IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException { + public int sendMessageBatch(String message, @NonNull UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException { return 0; } @@ -125,8 +126,9 @@ public JSONObject getCookies() { return null; } + @NonNull @Override - public AliasNetworkResponse sendAliasRequest(@NonNull String request) throws JSONException, IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException { + public AliasNetworkResponse sendAliasRequest(@NonNull String request, @NonNull UploadSettings uploadSettings) throws JSONException, IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException { return new AliasNetworkResponse(0); } From 004fe57c43a7edcafdce69e97d479a52e731fc15 Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Mon, 30 Sep 2024 13:29:40 -0500 Subject: [PATCH 03/13] Fix UploadTable create table DDL --- .../com/mparticle/internal/database/tables/UploadTable.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java b/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java index 2e21b3b19..c089ba959 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java +++ b/android-core/src/main/java/com/mparticle/internal/database/tables/UploadTable.java @@ -29,7 +29,8 @@ protected interface UploadTableColumns extends BaseColumns { UploadTableColumns.MESSAGE + " TEXT, " + UploadTableColumns.CREATED_AT + " INTEGER NOT NULL, " + UploadTableColumns.REQUEST_TYPE + " TEXT, " + - UploadTableColumns.SESSION_ID + " TEXT" + + UploadTableColumns.SESSION_ID + " TEXT, " + + UploadTableColumns.UPLOAD_SETTINGS + " TEXT" + ");"; static final String UPLOAD_ADD_UPLOAD_SETTINGS_COLUMN = "ALTER TABLE " + UploadTableColumns.TABLE_NAME + From 4ddba7e8f38290a503faed9d7228429d9f7b8de2 Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Tue, 1 Oct 2024 10:57:07 -0500 Subject: [PATCH 04/13] Finish workspace switching implementation --- .../main/java/com/mparticle/MParticle.java | 20 ++++++++++++++++++ .../com/mparticle/internal/ConfigManager.java | 21 +++++++++++++------ .../com/mparticle/internal/UserStorage.java | 21 +++++++++++++++++++ .../internal/database/UploadSettings.java | 1 - .../tables/MParticleDatabaseHelper.java | 8 ++++++- 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index 105fd4a27..ce9428fa0 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -42,6 +42,7 @@ import com.mparticle.internal.MParticleJSInterface; import com.mparticle.internal.MessageManager; import com.mparticle.internal.PushRegistrationHelper; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.database.services.MParticleDBManager; import com.mparticle.internal.database.tables.MParticleDatabaseHelper; import com.mparticle.internal.listeners.ApiClass; @@ -60,6 +61,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -188,6 +190,24 @@ 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()); + + // Check if we've switched workspaces on startup + UploadSettings lastUploadSettings = instance.mConfigManager.getLastUploadSettings(); + if (lastUploadSettings != null && !Objects.equals(lastUploadSettings.getApiKey(), options.getApiKey())) { + // Different workspace, so batch previous messages under old upload settings before starting + try { + instance.mMessageManager.mUploadHandler.prepareMessageUploads(lastUploadSettings); + } catch (Exception e) { + Logger.error(e, "Unable to create upload records for previous workspace"); + } + + // Delete the cached config + instance.mConfigManager.clearConfig(); + } + + // Cache the upload settings in case we switch workspaces on startup + instance.mConfigManager.setLastUploadSettings(instance.mConfigManager.getUploadSettings()); + instance.mMessageManager.refreshConfiguration(); instance.identify(options); if (options.hasLocationTracking()) { diff --git a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java index a8144aba3..e7eaae492 100644 --- a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java +++ b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java @@ -4,6 +4,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.net.Network; import android.os.Build; import androidx.annotation.NonNull; @@ -336,7 +337,7 @@ void saveConfigJson(JSONObject coreConfig, JSONArray kitConfig, String etag, Str } } - void clearConfig() { + public void clearConfig() { sPreferences.edit() .remove(CONFIG_JSON) .remove(CONFIG_JSON_TIMESTAMP) @@ -587,14 +588,14 @@ public void setLogUnhandledExceptions(boolean log) { sPreferences.edit().putBoolean(Constants.PrefKeys.REPORT_UNCAUGHT_EXCEPTIONS, log).apply(); } - // TODO: BEN - return null instead of setting empty strings? public UploadSettings getUploadSettings() { String apiKey = getApiKey(); String secret = getApiSecret(); - return new UploadSettings( - apiKey == null ? "" : apiKey, - secret == null ? "" : secret, - getNetworkOptions(), getActiveModuleIds(), getSupportedKitString()); + if (apiKey == null || secret == null) { + return null; + } + + return new UploadSettings(apiKey, secret, getNetworkOptions(), getActiveModuleIds(), getSupportedKitString()); } public String getApiKey() { @@ -1350,6 +1351,14 @@ public synchronized void setNetworkOptions(NetworkOptions networkOptions) { sPreferences.edit().remove(Constants.PrefKeys.NETWORK_OPTIONS).apply(); } + public UploadSettings getLastUploadSettings() { + return getUserStorage().getLastUploadSettings(); + } + + public void setLastUploadSettings(@NonNull UploadSettings uploadSettings) { + getUserStorage().setLastUploadSettings(uploadSettings); + } + @NonNull public String getWorkspaceToken() { return sPreferences.getString(WORKSPACE_TOKEN, ""); diff --git a/android-core/src/main/java/com/mparticle/internal/UserStorage.java b/android-core/src/main/java/com/mparticle/internal/UserStorage.java index 0190d89bb..dbf75c932 100644 --- a/android-core/src/main/java/com/mparticle/internal/UserStorage.java +++ b/android-core/src/main/java/com/mparticle/internal/UserStorage.java @@ -7,6 +7,11 @@ import android.net.UrlQuerySanitizer; import android.os.Build; +import androidx.annotation.NonNull; + +import com.mparticle.internal.database.UploadSettings; +import com.mparticle.networking.NetworkOptions; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -37,6 +42,7 @@ public class UserStorage { private static final String FIRST_SEEN_TIME = "mp::first_seen"; private static final String LAST_SEEN_TIME = "mp::last_seen"; private static final String DEFAULT_SEEN_TIME = "mp::default_seen_time"; + private static final String LAST_UPLOAD_SETTINGS = "mp::last_upload_settings"; static final int DEFAULT_BREADCRUMB_LIMIT = 50; @@ -317,6 +323,21 @@ private Long getDefaultSeenTime() { return getMParticleSharedPrefs(mContext).getLong(DEFAULT_SEEN_TIME, System.currentTimeMillis()); } + public UploadSettings getLastUploadSettings() { + String lastUploadSettingsJson = mPreferences.getString(LAST_UPLOAD_SETTINGS, null); + if (lastUploadSettingsJson != null) { + return UploadSettings.withJson(lastUploadSettingsJson); + } + return null; + } + + public void setLastUploadSettings(@NonNull UploadSettings uploadSettings) { + String lastUploadSettingsJson = uploadSettings.toJson(); + if (lastUploadSettingsJson != null) { + mPreferences.edit().putString(LAST_UPLOAD_SETTINGS, lastUploadSettingsJson).apply(); + } + } + void setLoggedInUser(boolean knownUser) { mPreferences.edit().putBoolean(KNOWN_USER, knownUser).apply(); } diff --git a/android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java b/android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java index eadb39353..0fb59497a 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java +++ b/android-core/src/main/java/com/mparticle/internal/database/UploadSettings.java @@ -14,7 +14,6 @@ public class UploadSettings { private final String mActiveKits; private final String mSupportedKits; - // TODO: BEN - Accept null values? public UploadSettings(@NonNull String apiKey, @NonNull String secret, @NonNull NetworkOptions networkOptions, @NonNull String activeKits, @NonNull String supportedKits) { mApiKey = apiKey; mSecret = secret; diff --git a/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java b/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java index 37965343b..ea7632bd4 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java +++ b/android-core/src/main/java/com/mparticle/internal/database/tables/MParticleDatabaseHelper.java @@ -11,6 +11,7 @@ import com.mparticle.internal.ConfigManager; import com.mparticle.internal.Constants; import com.mparticle.internal.Logger; +import com.mparticle.internal.database.UploadSettings; import com.mparticle.internal.database.services.SQLiteOpenHelperWrapper; import org.json.JSONException; @@ -164,6 +165,11 @@ private void removeGcmTable(SQLiteDatabase db) { private void upgradeUploadsTable(SQLiteDatabase db) { db.execSQL(UploadTable.UPLOAD_ADD_UPLOAD_SETTINGS_COLUMN); - // TODO: BEN - insert current upload settings + + // Insert current upload settings + UploadSettings uploadSettings = ConfigManager.getInstance(mContext).getUploadSettings(); + ContentValues values = new ContentValues(); + values.put(UploadTable.UploadTableColumns.UPLOAD_SETTINGS, uploadSettings.toJson()); + db.update(UploadTable.UploadTableColumns.TABLE_NAME, values, null, null); } } From d4f821703eb73d676a9bb42f21045d3f63d1796c Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Wed, 2 Oct 2024 14:36:21 -0500 Subject: [PATCH 05/13] Fix all existing unit and integration tests --- .../kotlin/com.mparticle/UploadMessageTest.kt | 3 +- .../AppStateManagerInstrumentedTest.kt | 1 - .../internal/database/UpgradeVersionTest.kt | 5 ++- .../networking/MParticleBaseClientImplTest.kt | 15 ++++--- .../networking/NetworkOptionsTest.kt | 30 +++++++++----- .../com.mparticle/networking/PinningTest.kt | 7 +++- .../mparticle/internal/MessageManager.java | 2 +- .../networking/MParticleBaseClientImpl.java | 2 +- .../mparticle/external/ApiVisibilityTest.kt | 2 +- .../internal/MParticleApiClientImplTest.kt | 22 +++++----- .../mparticle/internal/MessageHandlerTest.kt | 6 ++- .../mparticle/internal/UploadHandlerTest.kt | 41 +++++++++++++++---- .../main/java/com/mparticle/AccessUtils.java | 4 +- .../com/mparticle/internal/AccessUtils.java | 2 +- .../mparticle/testutils/BaseAbstractTest.java | 4 +- 15 files changed, 98 insertions(+), 48 deletions(-) diff --git a/android-core/src/androidTest/kotlin/com.mparticle/UploadMessageTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/UploadMessageTest.kt index 92dc4adab..490c3529d 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/UploadMessageTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/UploadMessageTest.kt @@ -8,6 +8,7 @@ import com.mparticle.internal.Constants import com.mparticle.internal.MPUtility import com.mparticle.internal.MParticleApiClientImpl.MPRampException import com.mparticle.internal.MParticleApiClientImpl.MPThrottleException +import com.mparticle.internal.database.UploadSettings import com.mparticle.testutils.BaseCleanStartedEachTest import com.mparticle.testutils.MPLatch import com.mparticle.testutils.TestingUtils @@ -36,7 +37,7 @@ class UploadMessageTest : BaseCleanStartedEachTest() { val matchingJSONEvents: MutableMap> = HashMap() AccessUtils.setMParticleApiClient(object : EmptyMParticleApiClient() { @Throws(IOException::class, MPThrottleException::class, MPRampException::class) - override fun sendMessageBatch(message: String): Int { + override fun sendMessageBatch(message: String, uploadSettings: UploadSettings): Int { handler.post { try { val jsonObject = JSONObject(message) diff --git a/android-core/src/androidTest/kotlin/com.mparticle/internal/AppStateManagerInstrumentedTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/internal/AppStateManagerInstrumentedTest.kt index 45f634fa3..534b4c7fa 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/internal/AppStateManagerInstrumentedTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/internal/AppStateManagerInstrumentedTest.kt @@ -50,7 +50,6 @@ class AppStateManagerInstrumentedTest : BaseCleanStartedEachTest() { } } checked[0] = true - latch.countDown() } catch (e: JSONException) { e.printStackTrace() } diff --git a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt index 3d2d95da4..208ea2bff 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt @@ -2,6 +2,8 @@ package com.mparticle.internal.database import android.database.sqlite.SQLiteDatabase import android.location.Location +import android.net.Network +import com.mparticle.internal.ConfigManager import com.mparticle.internal.InternalSession import com.mparticle.internal.database.services.BreadcrumbService import com.mparticle.internal.database.services.MessageService @@ -19,6 +21,7 @@ import com.mparticle.internal.database.tables.ReportingTableTest import com.mparticle.internal.database.tables.SessionTableTest import com.mparticle.internal.database.tables.UserAttributeTableTest import com.mparticle.internal.messages.BaseMPMessage +import com.mparticle.networking.NetworkOptions import com.mparticle.testutils.TestingUtils import org.json.JSONException import org.json.JSONObject @@ -90,7 +93,7 @@ class UpgradeVersionTest : BaseTableTest() { 1L ) SessionService.insertSession(db, message, "key", "", "", 1L) - UploadService.insertAliasRequest(db, "key", JSONObject().put("key", "value")) + UploadService.insertAliasRequest(db, JSONObject().put("key", "value"), UploadSettings("apiKey", "secret", NetworkOptions.builder().build(), "", "")) UserAttributesService.insertAttribute(db, "key", "value", 1L, false, 1L) // test to make sure there are values in the database diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt index ea1abb64c..eed499160 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt @@ -1,11 +1,13 @@ package com.mparticle.networking +import android.net.Network import android.net.Uri import com.mparticle.BuildConfig import com.mparticle.MParticle import com.mparticle.MParticleOptions import com.mparticle.internal.AccessUtils import com.mparticle.internal.ConfigManager +import com.mparticle.internal.database.UploadSettings import com.mparticle.testutils.BaseCleanInstallEachTest import org.junit.Assert import org.junit.Assert.assertEquals @@ -23,7 +25,7 @@ class MParticleBaseClientImplTest : BaseCleanInstallEachTest() { startMParticle(MParticleOptions.builder(mContext).credentials(apiKey, "secret")) val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { - defaultUrls[endpoint] = baseClientImpl.getUrl(endpoint, endpoint.name, false) + defaultUrls[endpoint] = baseClientImpl.getUrl(endpoint, endpoint.name, UploadSettings(apiKey, "secret", NetworkOptions.builder().build(), "", "")) } MParticle.setInstance(null) } @@ -147,9 +149,9 @@ class MParticleBaseClientImplTest : BaseCleanInstallEachTest() { .build() MParticle.start(options) val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl - map.forEach { key, value -> - mConfigManager?.setCredentials(key, value) - val prefix = mConfigManager?.podPrefix + map.forEach { (key, value) -> + mConfigManager.setCredentials(key, value) + val prefix = mConfigManager.getPodPrefix(key) assertEquals(value, prefix) assertEquals( "${NetworkOptionsManager.MP_URL_PREFIX}.$prefix.mparticle.com", @@ -215,13 +217,14 @@ class MParticleBaseClientImplTest : BaseCleanInstallEachTest() { ) .build() MParticle.start(options) + val uploadSettings = UploadSettings(apiKey, "secret", options.networkOptions, "", "") val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { - val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name) + val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name, uploadSettings) assertEquals(defaultUrls[endpoint].toString(), generatedUrl.defaultUrl.toString()) } for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { - val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name) + val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name, uploadSettings) Assert.assertNotEquals(defaultUrls[endpoint].toString(), generatedUrl.toString()) Assert.assertFalse(generatedUrl === generatedUrl.defaultUrl) assertEquals( diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt index 4c35066df..54c1c8ef7 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt @@ -1,8 +1,10 @@ package com.mparticle.networking +import android.net.Network import com.mparticle.MParticle import com.mparticle.MParticleOptions import com.mparticle.internal.AccessUtils +import com.mparticle.internal.database.UploadSettings import com.mparticle.testutils.BaseCleanInstallEachTest import org.junit.After import org.junit.Assert @@ -22,7 +24,7 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { startMParticle(MParticleOptions.builder(mContext).credentials(apiKey, "secret")) setClients() for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { - defaultUrls[endpoint] = mpClient.getUrl(endpoint, endpoint.name) + defaultUrls[endpoint] = mpClient.getUrl(endpoint, endpoint.name, UploadSettings(apiKey, "secret", NetworkOptions.builder().build(), "", "")) } MParticle.setInstance(null) } @@ -61,7 +63,7 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { var randIdentityPath = mRandomUtils.getAlphaString(10) Assert.assertEquals( "/v1/$randIdentityPath", - mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath).path + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath, null).path ) Assert.assertEquals( NetworkOptionsManager.MP_URL, @@ -84,7 +86,8 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { "/v1/$randIdentityPath", identityClient.getUrl( MParticleBaseClientImpl.Endpoint.IDENTITY, - randIdentityPath + randIdentityPath, + null ).path ) } @@ -139,7 +142,7 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { var randIdentityPath = mRandomUtils.getAlphaString(10) Assert.assertEquals( "/v1/$randIdentityPath", - mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath).path + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath, null).path ) Assert.assertEquals( audienceUrl, @@ -162,7 +165,8 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { "/v1/$randIdentityPath", identityClient.getUrl( MParticleBaseClientImpl.Endpoint.IDENTITY, - randIdentityPath + randIdentityPath, + null ).path ) @@ -183,7 +187,8 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { defaultUrls[MParticleBaseClientImpl.Endpoint.IDENTITY]?.path, mpClient.getUrl( MParticleBaseClientImpl.Endpoint.IDENTITY, - MParticleBaseClientImpl.Endpoint.IDENTITY.name + MParticleBaseClientImpl.Endpoint.IDENTITY.name, + null ).path ) } @@ -237,7 +242,7 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { var randIdentityPath = mRandomUtils.getAlphaString(10) Assert.assertEquals( "/v1/$randIdentityPath", - mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath).path + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath, null).path ) Assert.assertEquals( audienceUrl, @@ -260,7 +265,8 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { "/v1/$randIdentityPath", identityClient.getUrl( MParticleBaseClientImpl.Endpoint.IDENTITY, - randIdentityPath + randIdentityPath, + null ).path ) @@ -281,7 +287,8 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { defaultUrls[MParticleBaseClientImpl.Endpoint.IDENTITY]?.path, mpClient.getUrl( MParticleBaseClientImpl.Endpoint.IDENTITY, - MParticleBaseClientImpl.Endpoint.IDENTITY.name + MParticleBaseClientImpl.Endpoint.IDENTITY.name, + null ).path ) } @@ -360,7 +367,7 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { val randIdentityPath = mRandomUtils.getAlphaString(10) Assert.assertEquals( "/$randIdentityPath", - mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath).path + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath, null).path ) // test the that the Path is still the default one (make sure the overrideSubdirectory is not kicking in when it shouldn't) @@ -391,7 +398,8 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { identityPath, mpClient.getUrl( MParticleBaseClientImpl.Endpoint.IDENTITY, - MParticleBaseClientImpl.Endpoint.IDENTITY.name + MParticleBaseClientImpl.Endpoint.IDENTITY.name, + null ).path ) } diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt index 2122cd581..707081965 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt @@ -1,9 +1,11 @@ package com.mparticle.networking +import android.net.Network import androidx.test.platform.app.InstrumentationRegistry import com.mparticle.MParticle import com.mparticle.identity.IdentityApiRequest import com.mparticle.internal.AccessUtils +import com.mparticle.internal.database.UploadSettings import com.mparticle.testutils.AndroidUtils import com.mparticle.testutils.BaseCleanStartedEachTest import com.mparticle.testutils.MPLatch @@ -97,7 +99,7 @@ open class PinningTest : BaseCleanStartedEachTest() { latch.countDown() } try { - AccessUtils.getApiClient().sendMessageBatch(JSONObject().toString()) + AccessUtils.getApiClient().sendMessageBatch(JSONObject().toString(), UploadSettings("apiKey", "secret", NetworkOptions.builder().build(), "", "")) } catch (_: Exception) { } latch.await() @@ -105,8 +107,9 @@ open class PinningTest : BaseCleanStartedEachTest() { } companion object { + @JvmStatic @BeforeClass - fun beforeClass() { + fun beforeClass(): Unit { MParticle.reset(InstrumentationRegistry.getInstrumentation().context) } } diff --git a/android-core/src/main/java/com/mparticle/internal/MessageManager.java b/android-core/src/main/java/com/mparticle/internal/MessageManager.java index cd3fbf4af..c85d7aa8d 100644 --- a/android-core/src/main/java/com/mparticle/internal/MessageManager.java +++ b/android-core/src/main/java/com/mparticle/internal/MessageManager.java @@ -772,7 +772,7 @@ public String getApiKey() throws MParticleApiClientImpl.MPNoConfigException { @Override public UploadSettings getUploadSettings() throws MParticleApiClientImpl.MPNoConfigException { UploadSettings uploadSettings = mConfigManager.getUploadSettings(); - if (MPUtility.isEmpty(uploadSettings.getApiKey()) || MPUtility.isEmpty(uploadSettings.getSecret())) { + if (uploadSettings == null) { throw new MParticleApiClientImpl.MPNoConfigException(); } return uploadSettings; diff --git a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java index 77abff95f..9cb5ac8c8 100644 --- a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java +++ b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java @@ -127,7 +127,7 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, @Nullab String subdirectory; String pathPrefix; String pathPostfix; - boolean overridesSubdirectory = domainMapping.isOverridesSubdirectory(); + boolean overridesSubdirectory = domainMapping != null && domainMapping.isOverridesSubdirectory(); switch (endpoint) { case CONFIG: pathPrefix = SERVICE_VERSION_4 + "/"; 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 3017d6bbf..7f5b88619 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(65, publicMethodCount) + Assert.assertEquals(66, publicMethodCount) } @Test diff --git a/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt index bad34528d..e93b29fc4 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt @@ -4,11 +4,13 @@ import android.content.SharedPreferences import androidx.test.filters.LargeTest import com.mparticle.internal.MParticleApiClientImpl.MPConfigException import com.mparticle.internal.MParticleApiClientImpl.MPThrottleException +import com.mparticle.internal.database.UploadSettings import com.mparticle.mock.MockContext import com.mparticle.mock.MockSharedPreferences import com.mparticle.networking.MPConnection import com.mparticle.networking.MPUrl import com.mparticle.networking.MParticleBaseClientImpl +import com.mparticle.networking.NetworkOptions import org.json.JSONObject import org.junit.Assert import org.junit.Test @@ -211,7 +213,7 @@ class MParticleApiClientImplTest { Mockito.`when`(MPUtility.hmacSha256Encode(Mockito.anyString(), Mockito.anyString())) .thenReturn("encoded") try { - client.sendMessageBatch("") + client.sendMessageBatch("", configManager.uploadSettings) } catch (e: Exception) { if (e is MPThrottleException) { throw e @@ -223,7 +225,7 @@ class MParticleApiClientImplTest { ) var e: Exception? = null try { - client.sendMessageBatch("") + client.sendMessageBatch("", configManager.uploadSettings) } catch (cfe: MPThrottleException) { e = cfe } @@ -271,7 +273,7 @@ class MParticleApiClientImplTest { Mockito.`when`(MPUtility.hmacSha256Encode(Mockito.anyString(), Mockito.anyString())) .thenReturn("encoded") try { - client.sendAliasRequest("") + client.sendAliasRequest("", configManager.uploadSettings) } catch (e: Exception) { if (e is MPThrottleException) { throw e @@ -283,7 +285,7 @@ class MParticleApiClientImplTest { ) var e: Exception? = null try { - client.sendAliasRequest("") + client.sendAliasRequest("", configManager.uploadSettings) } catch (cfe: MPThrottleException) { e = cfe } @@ -306,8 +308,8 @@ class MParticleApiClientImplTest { Mockito.`when`(MPUtility.hmacSha256Encode(Mockito.anyString(), Mockito.anyString())) .thenReturn("encoded") try { - client.sendMessageBatch("") - client.sendAliasRequest("") + client.sendMessageBatch("", configManager.uploadSettings) + client.sendAliasRequest("", configManager.uploadSettings) } catch (e: Exception) { if (e is MPThrottleException) { throw e @@ -325,14 +327,14 @@ class MParticleApiClientImplTest { ) var ex: MPThrottleException? = null try { - client.sendMessageBatch("") + client.sendMessageBatch("", configManager.uploadSettings) } catch (e: Exception) { if (e is MPThrottleException) { throw e } } try { - client.sendAliasRequest("") + client.sendAliasRequest("", configManager.uploadSettings) } catch (e: MPThrottleException) { ex = e } @@ -349,14 +351,14 @@ class MParticleApiClientImplTest { ) ex = null try { - client.sendAliasRequest("") + client.sendAliasRequest("", configManager.uploadSettings) } catch (e: Exception) { if (e is MPThrottleException) { throw e } } try { - client.sendMessageBatch("") + client.sendMessageBatch("", configManager.uploadSettings) } catch (e: MPThrottleException) { ex = e } diff --git a/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt index 2de38d23b..80cdabf45 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt @@ -1,13 +1,16 @@ package com.mparticle.internal +import android.net.Network import android.os.Message import com.mparticle.MParticle import com.mparticle.MockMParticle import com.mparticle.internal.Constants.MessageKey import com.mparticle.internal.database.MPDatabase +import com.mparticle.internal.database.UploadSettings import com.mparticle.internal.database.services.MParticleDBManager import com.mparticle.internal.messages.MPAliasMessage import com.mparticle.mock.MockContext +import com.mparticle.networking.NetworkOptions import com.mparticle.testutils.AndroidUtils import com.mparticle.testutils.TestingUtils import junit.framework.TestCase @@ -37,6 +40,7 @@ class MessageHandlerTest { mConfigManager = MParticle.getInstance()?.Internal()?.configManager!! mMessageManager = Mockito.mock(MessageManager::class.java) Mockito.`when`(mMessageManager.apiKey).thenReturn("apiKey") + Mockito.`when`(mMessageManager.uploadSettings).thenReturn(UploadSettings("apiKey", "secret", NetworkOptions.builder().build(), "", "")) mParticleDatabaseManager = Mockito.mock(MParticleDBManager::class.java) handler = object : MessageHandler( mMessageManager, @@ -57,7 +61,7 @@ class MessageHandlerTest { val insertedAliasRequest = AndroidUtils.Mutable(null) Mockito.`when`(mConfigManager.deviceApplicationStamp).thenReturn("das") val database: MParticleDBManager = object : MParticleDBManager(MockContext()) { - override fun insertAliasRequest(apiKey: String, request: JSONObject) { + override fun insertAliasRequest(request: JSONObject, uploadSettings: UploadSettings) { insertedAliasRequest.value = request } diff --git a/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt index eb6d182ba..52287cbea 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt @@ -1,6 +1,7 @@ package com.mparticle.internal import android.content.Context +import android.net.Network import android.os.Message import com.mparticle.MParticle import com.mparticle.MockMParticle @@ -10,9 +11,11 @@ import com.mparticle.internal.MParticleApiClient.AliasNetworkResponse import com.mparticle.internal.MParticleApiClientImpl.MPRampException import com.mparticle.internal.MParticleApiClientImpl.MPThrottleException import com.mparticle.internal.database.MPDatabase +import com.mparticle.internal.database.UploadSettings import com.mparticle.internal.database.services.MParticleDBManager import com.mparticle.internal.messages.MPAliasMessage import com.mparticle.mock.MockContext +import com.mparticle.networking.NetworkOptions import com.mparticle.testutils.AndroidUtils import com.mparticle.testutils.RandomUtils import com.mparticle.testutils.TestingUtils @@ -119,10 +122,10 @@ class UploadHandlerTest { MParticleApiClientImpl::class.java ) val rampException = MPRampException() - Mockito.`when`(apiClient.sendMessageBatch(Mockito.anyString())).thenThrow(rampException) + Mockito.`when`(apiClient.sendMessageBatch(Mockito.anyString(), Mockito.any())).thenThrow(rampException) Mockito.`when`(handler.mParticleDBManager.deleteUpload(Mockito.anyInt())).thenReturn(1) handler.setApiClient(apiClient) - handler.uploadMessage(522, "") + handler.uploadMessage(522, "", mConfigManager.uploadSettings) } @Test @@ -175,14 +178,18 @@ class UploadHandlerTest { mockApiClient.sendAliasRequest( Mockito.any( String::class.java + ), + Mockito.any( + UploadSettings::class.java ) ) ).thenReturn(AliasNetworkResponse(0)) + Mockito.`when`(mConfigManager.uploadSettings).thenReturn(UploadSettings("apiKey", "secret", NetworkOptions.builder().build(), "", "")) uploadHandler.setApiClient(mockApiClient) TestCase.assertNull(deleteId.value) val aliasRequest = TestingUtils.getInstance().randomAliasRequest val request: JSONObject = MPAliasMessage(aliasRequest, "das", "apiKey") - uploadHandler.uploadAliasRequest(1, request.toString()) + uploadHandler.uploadAliasRequest(1, request.toString(), mConfigManager.uploadSettings) TestCase.assertNull(deleteId.value) } @@ -220,15 +227,20 @@ class UploadHandlerTest { mockApiClient.sendAliasRequest( Mockito.any( String::class.java + ), + Mockito.any( + UploadSettings::class.java ) ) ).thenReturn(AliasNetworkResponse(0)) + Mockito.`when`(mConfigManager.uploadSettings).thenReturn(UploadSettings("apiKey", "secret", NetworkOptions.builder().build(), "", "")) uploadHandler.setApiClient(mockApiClient) TestCase.assertNull(deletedUpload.value) val aliasRequest = TestingUtils.getInstance().randomAliasRequest uploadHandler.uploadAliasRequest( 1, - MPAliasMessage(aliasRequest, "das", "apiKey").toString() + MPAliasMessage(aliasRequest, "das", "apiKey").toString(), + mConfigManager.uploadSettings ) Assert.assertNotNull(deletedUpload.value) } @@ -267,13 +279,16 @@ class UploadHandlerTest { mockApiClient.sendAliasRequest( Mockito.any( String::class.java + ), + Mockito.any( + UploadSettings::class.java ) ) ).thenReturn(AliasNetworkResponse(202)) TestCase.assertNull(capturedResponse.value) var aliasRequest = TestingUtils.getInstance().randomAliasRequest var aliasRequestMessage = MPAliasMessage(aliasRequest, "das", "apiKey") - handler.uploadAliasRequest(1, aliasRequestMessage.toString()) + handler.uploadAliasRequest(1, aliasRequestMessage.toString(), mConfigManager.uploadSettings) capturedResponse.value?.isSuccessful?.let { Assert.assertTrue(it) } TestCase.assertNull(capturedResponse.value?.errorResponse) capturedResponse.value?.willRetry()?.let { Assert.assertFalse(it) } @@ -287,13 +302,16 @@ class UploadHandlerTest { mockApiClient.sendAliasRequest( Mockito.any( String::class.java + ), + Mockito.any( + UploadSettings::class.java ) ) ).thenReturn(AliasNetworkResponse(429)) TestCase.assertNull(capturedResponse.value) aliasRequest = TestingUtils.getInstance().randomAliasRequest aliasRequestMessage = MPAliasMessage(aliasRequest, "das", "apiKey") - handler.uploadAliasRequest(2, aliasRequestMessage.toString()) + handler.uploadAliasRequest(2, aliasRequestMessage.toString(), mConfigManager.uploadSettings) capturedResponse.value?.isSuccessful?.let { Assert.assertFalse(it) } TestCase.assertNull(capturedResponse.value?.errorResponse) capturedResponse.value?.willRetry()?.let { Assert.assertTrue(it) } @@ -308,13 +326,16 @@ class UploadHandlerTest { mockApiClient.sendAliasRequest( Mockito.any( String::class.java + ), + Mockito.any( + UploadSettings::class.java ) ) ).thenReturn(AliasNetworkResponse(400, error)) TestCase.assertNull(capturedResponse.value) aliasRequest = TestingUtils.getInstance().randomAliasRequest aliasRequestMessage = MPAliasMessage(aliasRequest, "das", "apiKey") - handler.uploadAliasRequest(3, aliasRequestMessage.toString()) + handler.uploadAliasRequest(3, aliasRequestMessage.toString(), mConfigManager.uploadSettings) capturedResponse.value?.isSuccessful?.let { Assert.assertFalse(it) } Assert.assertEquals(capturedResponse.value?.errorResponse, error) capturedResponse.value?.willRetry()?.let { Assert.assertFalse(it) } @@ -337,6 +358,7 @@ class UploadHandlerTest { ConfigManager::class.java ) Mockito.`when`(mockConfigManager.uploadInterval).thenReturn(100) + Mockito.`when`(mockConfigManager.uploadSettings).thenReturn(UploadSettings("apiKey", "secret", NetworkOptions.builder().build(), "", "")) val mockAppStateManager = Mockito.mock( AppStateManager::class.java ) @@ -354,6 +376,9 @@ class UploadHandlerTest { mockApiClient.sendMessageBatch( Mockito.any( String::class.java + ), + Mockito.any( + UploadSettings::class.java ) ) ).thenReturn(200) @@ -409,7 +434,7 @@ class UploadHandlerTest { } @Throws(Exception::class) - override fun prepareMessageUploads() { + override fun prepareMessageUploads(uploadSettings: UploadSettings) { prepareMessageUploadsCalledCount++ } diff --git a/testutils/src/main/java/com/mparticle/AccessUtils.java b/testutils/src/main/java/com/mparticle/AccessUtils.java index 1ef17d501..3ea77bb58 100644 --- a/testutils/src/main/java/com/mparticle/AccessUtils.java +++ b/testutils/src/main/java/com/mparticle/AccessUtils.java @@ -15,8 +15,8 @@ public class AccessUtils { - public static void reset(Context context, boolean deleteDatabase) { - MParticle.reset(context, deleteDatabase); + public static void reset(Context context, boolean deleteDatabase, boolean switchingWorkspaces) { + MParticle.reset(context, deleteDatabase, switchingWorkspaces); } /** diff --git a/testutils/src/main/java/com/mparticle/internal/AccessUtils.java b/testutils/src/main/java/com/mparticle/internal/AccessUtils.java index 5e6502be8..9302d5512 100644 --- a/testutils/src/main/java/com/mparticle/internal/AccessUtils.java +++ b/testutils/src/main/java/com/mparticle/internal/AccessUtils.java @@ -112,7 +112,7 @@ public void fetchConfig(boolean force) throws IOException, MParticleApiClientImp } @Override - public int sendMessageBatch(String message, @NonNull UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException { + public int sendMessageBatch(@NonNull String message, @NonNull UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException, MParticleApiClientImpl.MPRampException { return 0; } diff --git a/testutils/src/main/java/com/mparticle/testutils/BaseAbstractTest.java b/testutils/src/main/java/com/mparticle/testutils/BaseAbstractTest.java index 83d74b53c..ec75c28fa 100644 --- a/testutils/src/main/java/com/mparticle/testutils/BaseAbstractTest.java +++ b/testutils/src/main/java/com/mparticle/testutils/BaseAbstractTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.database.Cursor; @@ -84,6 +85,7 @@ protected void startMParticle() throws InterruptedException { startMParticle(MParticleOptions.builder(mContext)); } + @SuppressLint("MParticleInitialization") protected void startMParticle(MParticleOptions.Builder optionsBuilder) throws InterruptedException { MParticle.setInstance(null); final CountDownLatch latch = new MPLatch(1); @@ -128,7 +130,7 @@ protected void goToForeground() { } protected void clearStorage() { - com.mparticle.AccessUtils.reset(mContext, false); + com.mparticle.AccessUtils.reset(mContext, false, false); MPDatabase database = new MParticleDBManager(mContext).getDatabase(); List tableNames = getAllTables(database); for (String tableName : tableNames) { From 1bebc82764683df336f7a0222c8118d9df41536d Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Mon, 7 Oct 2024 11:08:05 -0500 Subject: [PATCH 06/13] Add integration tests --- .../kotlin/com.mparticle/MParticleTest.kt | 92 +++++++++++++++++++ .../main/java/com/mparticle/MParticle.java | 9 ++ 2 files changed, 101 insertions(+) diff --git a/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt index fce254595..4295c5050 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt @@ -12,6 +12,7 @@ import com.mparticle.internal.KitFrameworkWrapper import com.mparticle.internal.MParticleJSInterface import com.mparticle.internal.MessageManager import com.mparticle.internal.PushRegistrationHelper.PushRegistration +import com.mparticle.internal.database.services.UploadService import com.mparticle.networking.Matcher import com.mparticle.networking.MockServer.JSONMatch import com.mparticle.testutils.AndroidUtils @@ -329,6 +330,97 @@ class MParticleTest : BaseCleanStartedEachTest() { Assert.assertNull(MParticle.getInstance()!!.mMessageManager.location) } + @Test + fun testSwitchWorkspacesCredentials() { + MParticle.setInstance(null) + + val optionsBuilder = MParticleOptions.builder(mContext) + optionsBuilder.apiKey = "apiKey1" + optionsBuilder.apiSecret = "apiSecret1" + val options = optionsBuilder.build() + + MParticle.start(options) + val instance1 = MParticle.getInstance() + + Assert.assertEquals("apiKey1", instance1!!.mConfigManager.apiKey) + Assert.assertEquals("apiSecret1", instance1.mConfigManager.apiSecret) + + val switchOptionsBuilder = MParticleOptions.builder(mContext) + switchOptionsBuilder.apiKey = "apiKey2" + switchOptionsBuilder.apiSecret = "apiSecret2" + val switchOptions = switchOptionsBuilder.build() + + MParticle.switchWorkspace(switchOptions) + val instance2 = MParticle.getInstance() + + Assert.assertEquals("apiKey2", instance2!!.mConfigManager.apiKey) + Assert.assertEquals("apiSecret2", instance2.mConfigManager.apiSecret) + } + + @Test + fun testSwitchWorkspacesUploadSettings() { + MParticle.setInstance(null) + + val optionsBuilder = MParticleOptions.builder(mContext) + optionsBuilder.apiKey = "apiKey1" + optionsBuilder.apiSecret = "apiSecret1" + val options = optionsBuilder.build() + + MParticle.start(options) + val instance1 = MParticle.getInstance() + + val eventsUrl1 = mServer.Endpoints().eventsUrl + Assert.assertEquals(true, eventsUrl1.path.contains(options.apiKey)) + + Assert.assertEquals("apiKey1", instance1!!.mConfigManager.apiKey) + Assert.assertEquals("apiSecret1", instance1.mConfigManager.apiSecret) + val event1 = MPEvent.Builder("event 1").build() + instance1.logEvent(event1) + + val readyUploads1 = UploadService.getReadyUploads(instance1.mDatabaseManager.database) + Assert.assertEquals(0, readyUploads1.count()) + + val switchOptionsBuilder = MParticleOptions.builder(mContext) + switchOptionsBuilder.apiKey = "apiKey2" + switchOptionsBuilder.apiSecret = "apiSecret2" + val switchOptions = switchOptionsBuilder.build() + + MParticle.switchWorkspace(switchOptions) + val instance2 = MParticle.getInstance() + + val eventsUrl2 = mServer.Endpoints().eventsUrl + Assert.assertTrue(eventsUrl2.path.contains(switchOptions.apiKey)) + + val event2 = MPEvent.Builder("event 2").build() + instance2!!.logEvent(event2) + + // TODO: https://go.mparticle.com/work/SQDSDKS-6840 + val latch: CountDownLatch = MPLatch(1) + val received = AndroidUtils.Mutable(false) + mServer.waitForVerify( + Matcher(eventsUrl2).bodyMatch( + JSONMatch { jsonObject -> + val hasEvent2 = jsonObject.optJSONArray("msgs") + ?.toList() + ?.filterIsInstance() + ?.any { it.optString("n") == event2.eventName } + Assert.assertTrue(hasEvent2 ?: false) + + return@JSONMatch hasEvent2 ?: false + } + ) + ) { + received.value = true + // TODO: https://go.mparticle.com/work/SQDSDKS-6842 +// latch.countDown() + } + + instance2.upload() + + latch.await() + Assert.assertTrue(received.value) + } + @Throws(JSONException::class, InterruptedException::class) private fun testReset(resetRunnable: Runnable) { for (i in 0..9) { diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index ce9428fa0..76917e463 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -281,6 +281,15 @@ public static void setInstance(@Nullable MParticle instance) { MParticle.instance = instance; } + /** + * Switch the SDK to a new API key and secret. + * Will first batch all events that have not been sent to mParticle into upload records, + * then all SDK state including user defaults, database (except uploads), etc will be + * completely reset. After that, {@link #start(MParticleOptions)} )} will be called with + * the new key and secret and the SDK will initialize again as if it is a new app launch. + * + @param options Required to initialize the SDK properly + */ public static void switchWorkspace(@NonNull MParticleOptions options) { if (instance != null) { // End session if active From 9df8fced9999a5ad55f4d842a34413f0170493e8 Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Mon, 7 Oct 2024 11:08:20 -0500 Subject: [PATCH 07/13] Fix Kotlin linter --- .../com.mparticle/internal/database/UpgradeVersionTest.kt | 2 -- .../com.mparticle/networking/MParticleBaseClientImplTest.kt | 1 - .../kotlin/com.mparticle/networking/NetworkOptionsTest.kt | 1 - .../androidTest/kotlin/com.mparticle/networking/PinningTest.kt | 3 +-- .../com/mparticle/internal/MParticleApiClientImplTest.kt | 2 -- .../test/kotlin/com/mparticle/internal/MessageHandlerTest.kt | 1 - .../test/kotlin/com/mparticle/internal/UploadHandlerTest.kt | 1 - 7 files changed, 1 insertion(+), 10 deletions(-) diff --git a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt index 208ea2bff..4a6d64850 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/UpgradeVersionTest.kt @@ -2,8 +2,6 @@ package com.mparticle.internal.database import android.database.sqlite.SQLiteDatabase import android.location.Location -import android.net.Network -import com.mparticle.internal.ConfigManager import com.mparticle.internal.InternalSession import com.mparticle.internal.database.services.BreadcrumbService import com.mparticle.internal.database.services.MessageService diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt index eed499160..2b8cdff83 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt @@ -1,6 +1,5 @@ package com.mparticle.networking -import android.net.Network import android.net.Uri import com.mparticle.BuildConfig import com.mparticle.MParticle diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt index 54c1c8ef7..6a5fc774e 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt @@ -1,6 +1,5 @@ package com.mparticle.networking -import android.net.Network import com.mparticle.MParticle import com.mparticle.MParticleOptions import com.mparticle.internal.AccessUtils diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt index 707081965..11080b67b 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/PinningTest.kt @@ -1,6 +1,5 @@ package com.mparticle.networking -import android.net.Network import androidx.test.platform.app.InstrumentationRegistry import com.mparticle.MParticle import com.mparticle.identity.IdentityApiRequest @@ -109,7 +108,7 @@ open class PinningTest : BaseCleanStartedEachTest() { companion object { @JvmStatic @BeforeClass - fun beforeClass(): Unit { + fun beforeClass() { MParticle.reset(InstrumentationRegistry.getInstrumentation().context) } } diff --git a/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt index e93b29fc4..8ab2ed303 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/MParticleApiClientImplTest.kt @@ -4,13 +4,11 @@ import android.content.SharedPreferences import androidx.test.filters.LargeTest import com.mparticle.internal.MParticleApiClientImpl.MPConfigException import com.mparticle.internal.MParticleApiClientImpl.MPThrottleException -import com.mparticle.internal.database.UploadSettings import com.mparticle.mock.MockContext import com.mparticle.mock.MockSharedPreferences import com.mparticle.networking.MPConnection import com.mparticle.networking.MPUrl import com.mparticle.networking.MParticleBaseClientImpl -import com.mparticle.networking.NetworkOptions import org.json.JSONObject import org.junit.Assert import org.junit.Test diff --git a/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt index 80cdabf45..b919f6100 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/MessageHandlerTest.kt @@ -1,6 +1,5 @@ package com.mparticle.internal -import android.net.Network import android.os.Message import com.mparticle.MParticle import com.mparticle.MockMParticle diff --git a/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt index 52287cbea..d01c8180e 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt @@ -1,7 +1,6 @@ package com.mparticle.internal import android.content.Context -import android.net.Network import android.os.Message import com.mparticle.MParticle import com.mparticle.MockMParticle From 83047eb9fe4a2dc930a9fd5098f909e2066e5a66 Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Mon, 7 Oct 2024 11:17:35 -0500 Subject: [PATCH 08/13] Remove commented out gradle changes --- android-core/build.gradle | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/android-core/build.gradle b/android-core/build.gradle index b63170578..e4b7d1df9 100644 --- a/android-core/build.gradle +++ b/android-core/build.gradle @@ -1,11 +1,3 @@ -//plugins { -//// id 'org.jetbrains.kotlin.plugin.serialization' version '2.0.20' -//// id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.0' -// id 'com.android.library' -// id 'kotlin-android' -//} -//apply from: '../scripts/maven.gradle' - ext { kitDescription = 'Core mParticle SDK supporting only server-side integrations.' } @@ -144,9 +136,6 @@ task generateSourcesJar(type: Jar) { } dependencies { -// compileOnly 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1' -// compileOnly 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0' - //noinspection GradleCompatible compileOnly 'com.google.firebase:firebase-messaging:[10.2.1, )' compileOnly 'com.android.installreferrer:installreferrer:[1.0, )' From 303397a72a5050522f5d76071ffa21c7924d829f Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Mon, 7 Oct 2024 11:22:30 -0500 Subject: [PATCH 09/13] Remove unused commented out gradle configs --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index b37835779..af0f34519 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,6 @@ buildscript { plugins { id "org.sonarqube" version "3.5.0.2730" id "org.jlleitschuh.gradle.ktlint" version "11.2.0" -// id 'org.jetbrains.kotlin.jvm' version '2.0.20' apply false -// id 'org.jetbrains.kotlin.jvm' version '1.8.0' apply false } sonarqube { From c9734407ab61d4a7db88dfc78b236314e4ca6954 Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Wed, 30 Oct 2024 15:21:00 -0500 Subject: [PATCH 10/13] Update android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java Co-authored-by: Mansi-mParticle <159845845+Mansi-mParticle@users.noreply.github.com> Signed-off-by: Ben Baron --- .../java/com/mparticle/internal/MParticleApiClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java b/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java index 2df09756e..6646bf853 100644 --- a/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java +++ b/android-core/src/main/java/com/mparticle/internal/MParticleApiClientImpl.java @@ -225,7 +225,7 @@ public JSONObject fetchAudiences() { public int sendMessageBatch(@NonNull String message, @NonNull UploadSettings uploadSettings) throws IOException, MPThrottleException, MPRampException { checkThrottleTime(Endpoint.EVENTS); checkRampValue(); - MPUrl eventUrl = getUrl(Endpoint.EVENTS,null, uploadSettings); + MPUrl eventUrl = getUrl(Endpoint.EVENTS, null, uploadSettings); MPConnection connection = eventUrl.openConnection(); connection.setConnectTimeout(mConfigManager.getConnectionTimeout()); connection.setReadTimeout(mConfigManager.getConnectionTimeout()); From 406f510bd3f02369b6496b9417b952b23136a19e Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Mon, 18 Nov 2024 15:03:13 -0500 Subject: [PATCH 11/13] Update android-core/src/main/java/com/mparticle/MParticle.java Co-authored-by: Sam Dozor Signed-off-by: Ben Baron --- android-core/src/main/java/com/mparticle/MParticle.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index 76917e463..8dd0631ff 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -288,6 +288,8 @@ public static void setInstance(@Nullable MParticle instance) { * completely reset. After that, {@link #start(MParticleOptions)} )} will be called with * the new key and secret and the SDK will initialize again as if it is a new app launch. * + * Warning: this method will make synchronous calls into SQLite and disk + * @param options Required to initialize the SDK properly */ public static void switchWorkspace(@NonNull MParticleOptions options) { From 70266b07821a06a8a132b5421f31ae8efeef7239 Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Wed, 20 Nov 2024 14:50:38 -0500 Subject: [PATCH 12/13] Improve threading based on feedback --- .../main/java/com/mparticle/MParticle.java | 54 +++++++++++++------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index 8dd0631ff..fd21fc073 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -288,31 +288,51 @@ public static void setInstance(@Nullable MParticle instance) { * completely reset. After that, {@link #start(MParticleOptions)} )} will be called with * the new key and secret and the SDK will initialize again as if it is a new app launch. * - * Warning: this method will make synchronous calls into SQLite and disk - * @param options Required to initialize the SDK properly */ public static void switchWorkspace(@NonNull MParticleOptions options) { - if (instance != null) { - // End session if active - if (instance.isSessionActive()) { - instance.endSession(); - } + synchronized (MParticle.class) { + MParticle localInstance = instance; + if (localInstance == null) { + performWorkspaceSwitch(options); + } else { + // End session if active + if (localInstance.isSessionActive()) { + localInstance.endSession(); + } - // Batch any remaining messages into upload records - try { - instance.mMessageManager.mUploadHandler.prepareMessageUploads(instance.mConfigManager.getUploadSettings()); - } catch (Exception e) { - Logger.error(e, "Unable to create upload records before switching workspaces"); + // Wait for the message thread to finish before processing upload records + localInstance.mMessageManager.getMessageHandler().post(() -> { + // Process the upload records on the upload thread + localInstance.mMessageManager.mUploadHandler.post(() -> { + try { + UploadSettings uploadSettings = localInstance.mConfigManager.getUploadSettings(); + localInstance.mMessageManager.mUploadHandler.prepareMessageUploads(uploadSettings); + } catch (Exception e) { + Logger.error(e, "Unable to create upload records before switching workspaces"); + } + + // + performWorkspaceSwitch(options); + }); + }); } } + } + + private static void performWorkspaceSwitch(@NonNull MParticleOptions options) { + final HandlerThread handlerThread = new HandlerThread("mParticleSwitchWorkspaceHandler"); + handlerThread.start(); + new Handler(handlerThread.getLooper()).post(() -> { + // Reset everything except for uploads table + resetForSwitchingWorkspaces(options.getContext()); - // Reset everything except for uploads table - reset(options.getContext(), false, true); + // Restart the SDK using new options + instance = null; + start(options); - // Restart the SDK using new options - instance = null; - start(options); + handlerThread.quit(); + }); } /** From 299a2650a19cbc83025e5fdcb7765dfe10c1f0fb Mon Sep 17 00:00:00 2001 From: Ben Baron Date: Mon, 25 Nov 2024 13:34:18 -0500 Subject: [PATCH 13/13] Fix unit tests --- .../kotlin/com.mparticle/MParticleTest.kt | 32 ++++--------------- .../main/java/com/mparticle/MParticle.java | 1 - 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt index 4295c5050..b9a66e949 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/MParticleTest.kt @@ -26,6 +26,7 @@ import org.junit.Test import java.io.File import java.util.Arrays import java.util.concurrent.CountDownLatch +import kotlin.test.assertTrue class MParticleTest : BaseCleanStartedEachTest() { private val configResponse = @@ -351,6 +352,8 @@ class MParticleTest : BaseCleanStartedEachTest() { val switchOptions = switchOptionsBuilder.build() MParticle.switchWorkspace(switchOptions) + + Thread.sleep(5000) val instance2 = MParticle.getInstance() Assert.assertEquals("apiKey2", instance2!!.mConfigManager.apiKey) @@ -386,6 +389,9 @@ class MParticleTest : BaseCleanStartedEachTest() { val switchOptions = switchOptionsBuilder.build() MParticle.switchWorkspace(switchOptions) + + Thread.sleep(5000) + val instance2 = MParticle.getInstance() val eventsUrl2 = mServer.Endpoints().eventsUrl @@ -394,31 +400,7 @@ class MParticleTest : BaseCleanStartedEachTest() { val event2 = MPEvent.Builder("event 2").build() instance2!!.logEvent(event2) - // TODO: https://go.mparticle.com/work/SQDSDKS-6840 - val latch: CountDownLatch = MPLatch(1) - val received = AndroidUtils.Mutable(false) - mServer.waitForVerify( - Matcher(eventsUrl2).bodyMatch( - JSONMatch { jsonObject -> - val hasEvent2 = jsonObject.optJSONArray("msgs") - ?.toList() - ?.filterIsInstance() - ?.any { it.optString("n") == event2.eventName } - Assert.assertTrue(hasEvent2 ?: false) - - return@JSONMatch hasEvent2 ?: false - } - ) - ) { - received.value = true - // TODO: https://go.mparticle.com/work/SQDSDKS-6842 -// latch.countDown() - } - - instance2.upload() - - latch.await() - Assert.assertTrue(received.value) + // TODO: Improvements to mock server to add more comprehensive testing on this - https://go.mparticle.com/work/SQDSDKS-6840 } @Throws(JSONException::class, InterruptedException::class) diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index fd21fc073..e78702954 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -312,7 +312,6 @@ public static void switchWorkspace(@NonNull MParticleOptions options) { Logger.error(e, "Unable to create upload records before switching workspaces"); } - // performWorkspaceSwitch(options); }); });