From 2973b4d0ceb44bf307758d38ae848e57383b284f Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Fri, 17 Sep 2021 09:22:06 -0700 Subject: [PATCH 01/13] add intercom dependencies --- samples/kotlin-android-app-destinations/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/kotlin-android-app-destinations/build.gradle b/samples/kotlin-android-app-destinations/build.gradle index ca4ea570..5925edfc 100644 --- a/samples/kotlin-android-app-destinations/build.gradle +++ b/samples/kotlin-android-app-destinations/build.gradle @@ -47,6 +47,10 @@ dependencies { // When using the BoM, you don't specify versions in Firebase library dependencies implementation 'com.google.firebase:firebase-analytics-ktx' + // Intercom + implementation 'io.intercom.android:intercom-sdk-base:10.1.1' + implementation 'io.intercom.android:intercom-sdk-fcm:10.1.1' + implementation project(':android') implementation 'androidx.multidex:multidex:2.0.1' From b00ae383dd2885d28abd3e908bb1c6ff0747482c Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Fri, 17 Sep 2021 09:22:15 -0700 Subject: [PATCH 02/13] add intercom destination --- .../plugins/IntercomDestination.kt | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt new file mode 100644 index 00000000..1c3ca037 --- /dev/null +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -0,0 +1,221 @@ +package com.segment.analytics.destinations.plugins + +import android.app.Application +import com.segment.analytics.kotlin.android.plugins.AndroidLifecycle +import com.segment.analytics.kotlin.core.* +import com.segment.analytics.kotlin.core.platform.DestinationPlugin +import com.segment.analytics.kotlin.core.utilities.putAll +import com.segment.analytics.kotlin.core.utilities.safeJsonObject +import io.intercom.android.sdk.Company +import io.intercom.android.sdk.Intercom +import io.intercom.android.sdk.UserAttributes +import io.intercom.android.sdk.identity.Registration +import kotlinx.serialization.json.* + +class IntercomDestination( + private val application: Application, + private val intercomApiKey: String, + private val intercomAppId: String +): DestinationPlugin(), AndroidLifecycle { + + override val key: String = "Intercom" + lateinit var intercom: Intercom + private set + + // Intercom common specced attributes + private val NAME = "name" + private val CREATED_AT = "createdAt" + private val COMPANY = "company" + private val PRICE = "price" + private val AMOUNT = "amount" + private val CURRENCY = "currency" + + // Intercom specced user attributes + private val EMAIL = "email" + private val PHONE = "phone" + private val LANGUAGE_OVERRIDE = "languageOverride" + private val UNSUBSCRIBED_FROM_EMAILS = "unsubscribedFromEmails" + + // Intercom specced group attributes + private val MONTHLY_SPEND = "monthlySpend" + private val PLAN = "plan" + + // Segment specced properties + private val REVENUE = "revenue" + private val TOTAL = "total" + + override fun setup(analytics: Analytics) { + super.setup(analytics) + + Intercom.initialize(application, intercomApiKey, intercomAppId) + this.intercom = Intercom.client() + } + + override fun track(payload: TrackEvent): BaseEvent? { + val result = super.track(payload) + + val eventName = payload.event + val properties = payload.properties + + if (!properties.isNullOrEmpty()) { + val price = buildJsonObject{ + val amount = properties[REVENUE] ?: properties[TOTAL] + amount?.let { + if (it is Number) { + put(AMOUNT, it.toDouble() * 100) + } + } + + properties[CURRENCY]?.let { + put(CURRENCY, it.toString()) + } + } + + val event = buildJsonObject { + if (!price.isNullOrEmpty()) { + put(PRICE, price) + } + + properties.forEach { + if (it.key !in arrayOf("products", REVENUE, TOTAL, CURRENCY) + && it.value is JsonPrimitive) { + put(it.key, it.value) + } + } + } + + intercom.logEvent(eventName, event) + } + + return result + } + + override fun identify(payload: IdentifyEvent): BaseEvent? { + val result = super.identify(payload) + + val userId = payload.userId + if (userId.isEmpty()) { + intercom.registerUnidentifiedUser() + } + else { + val registration = Registration.create().withUserId(userId) + intercom.registerIdentifiedUser(registration) + } + + payload.integrations["Intercom"]?.safeJsonObject?.let { intercomOptions -> + intercomOptions["userHash"]?.let { + val str = it.toString() + if (str.isNotEmpty()) { + intercom.setUserHash(str) + } + } + + if (!payload.traits.isNullOrEmpty() && intercomOptions.isNotEmpty()) { + setUserAttributes(payload.traits, intercomOptions) + } + else { + setUserAttributes(payload.traits, null) + } + } + + return result + } + + override fun group(payload: GroupEvent): BaseEvent? { + val result = super.group(payload) + + if (payload.groupId.isNotEmpty()) { + val traits = buildJsonObject { + putAll(payload.traits) + put("id", payload.groupId) + } + val company = setCompany(traits) + val userAttributes = UserAttributes.Builder() + .withCompany(company) + .build() + intercom.updateUser(userAttributes) + } + + return result + } + + override fun reset() { + super.reset() + intercom.logout() + } + + private fun setUserAttributes(traits: Traits, intercomOptions: JsonObject?) { + val builder = UserAttributes.Builder() + .withName(traits[NAME].toString()) + .withEmail(traits[EMAIL].toString()) + .withPhone(traits[PHONE].toString()) + + intercomOptions?.let { + builder.withLanguageOverride(it[LANGUAGE_OVERRIDE].toString()) + + it[CREATED_AT]?.let { createdAt -> + if (createdAt is Number) { + builder.withSignedUpAt(createdAt.toLong()) + } + } + + it[UNSUBSCRIBED_FROM_EMAILS]?.let { unsubscribed -> + if (unsubscribed is JsonPrimitive) { + builder.withUnsubscribedFromEmails(unsubscribed.jsonPrimitive.booleanOrNull) + } + } + } + + traits[COMPANY]?.let { + if (it is JsonObject) { + val company = setCompany(it) + builder.withCompany(company) + } + } + + traits.forEach { + if (it.value is JsonPrimitive && + it.key !in arrayOf(NAME, EMAIL, PHONE, "userId", "anonymousId")) { + builder.withCustomAttribute(it.key, it.value) + } + } + + intercom.updateUser(builder.build()) + } + + private fun setCompany(traits: JsonObject): Company { + val builder = Company.Builder() + traits["id"]?.let { + builder.withCompanyId(it.toString()) + } ?: return builder.build() + + traits[NAME]?.let { + builder.withName(it.toString()) + } + + traits[CREATED_AT]?.let { + if (it is JsonPrimitive) { + builder.withCreatedAt(it.jsonPrimitive.longOrNull) + } + } + + traits[MONTHLY_SPEND]?.let { + if (it is Number) { + builder.withMonthlySpend(it.toInt()) + } + } + + traits[PLAN]?.let { + builder.withPlan(it.toString()) + } + + traits.forEach { + if (it.value is JsonPrimitive && + it.key !in arrayOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN)) { + builder.withCustomAttribute(it.key, it.value) + } + } + + return builder.build() + } +} \ No newline at end of file From d60b709ba80ac4b3740c5d55c4e7951f453ba8a8 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Fri, 17 Sep 2021 12:11:11 -0700 Subject: [PATCH 03/13] fix initialization issue --- .../plugins/IntercomDestination.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index 1c3ca037..e7568ff1 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -4,6 +4,8 @@ import android.app.Application import com.segment.analytics.kotlin.android.plugins.AndroidLifecycle import com.segment.analytics.kotlin.core.* import com.segment.analytics.kotlin.core.platform.DestinationPlugin +import com.segment.analytics.kotlin.core.platform.Plugin +import com.segment.analytics.kotlin.core.utilities.getString import com.segment.analytics.kotlin.core.utilities.putAll import com.segment.analytics.kotlin.core.utilities.safeJsonObject import io.intercom.android.sdk.Company @@ -13,12 +15,12 @@ import io.intercom.android.sdk.identity.Registration import kotlinx.serialization.json.* class IntercomDestination( - private val application: Application, - private val intercomApiKey: String, - private val intercomAppId: String + private val application: Application ): DestinationPlugin(), AndroidLifecycle { override val key: String = "Intercom" + private var mobileApiKey: String = "" + private var appId: String = "" lateinit var intercom: Intercom private set @@ -44,10 +46,17 @@ class IntercomDestination( private val REVENUE = "revenue" private val TOTAL = "total" - override fun setup(analytics: Analytics) { - super.setup(analytics) + override fun update(settings: Settings, type: Plugin.UpdateType) { + // if we've already set up this singleton SDK, can't do it again, so skip. + if (type != Plugin.UpdateType.Initial) return + super.update(settings, type) - Intercom.initialize(application, intercomApiKey, intercomAppId) + settings.integrations[key]?.jsonObject?.let { + mobileApiKey = it.getString("mobileApiKey") ?: "" + appId = it.getString("appId") ?: "" + } + + Intercom.initialize(application, mobileApiKey, appId) this.intercom = Intercom.client() } From ea2d3e9fc98a65d70d75b6434935def33b785dd1 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Fri, 17 Sep 2021 12:15:56 -0700 Subject: [PATCH 04/13] add logs --- .../destinations/plugins/IntercomDestination.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index e7568ff1..1e86a029 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -5,6 +5,7 @@ import com.segment.analytics.kotlin.android.plugins.AndroidLifecycle import com.segment.analytics.kotlin.core.* import com.segment.analytics.kotlin.core.platform.DestinationPlugin import com.segment.analytics.kotlin.core.platform.Plugin +import com.segment.analytics.kotlin.core.platform.plugins.log import com.segment.analytics.kotlin.core.utilities.getString import com.segment.analytics.kotlin.core.utilities.putAll import com.segment.analytics.kotlin.core.utilities.safeJsonObject @@ -94,6 +95,11 @@ class IntercomDestination( } intercom.logEvent(eventName, event) + analytics.log("Intercom.client().logEvent($eventName, $event)") + } + else { + intercom.logEvent(eventName) + analytics.log("Intercom.client().logEvent($eventName)") } return result @@ -105,10 +111,12 @@ class IntercomDestination( val userId = payload.userId if (userId.isEmpty()) { intercom.registerUnidentifiedUser() + analytics.log("Intercom.client().registerUnidentifiedUser()") } else { val registration = Registration.create().withUserId(userId) intercom.registerIdentifiedUser(registration) + analytics.log("Intercom.client().registerIdentifiedUser(registration)") } payload.integrations["Intercom"]?.safeJsonObject?.let { intercomOptions -> @@ -151,6 +159,7 @@ class IntercomDestination( override fun reset() { super.reset() intercom.logout() + analytics.log("Intercom.client().reset()") } private fun setUserAttributes(traits: Traits, intercomOptions: JsonObject?) { @@ -190,6 +199,7 @@ class IntercomDestination( } intercom.updateUser(builder.build()) + analytics.log("Intercom.client().updateUser(userAttributes)") } private fun setCompany(traits: JsonObject): Company { From d736d0710451b22457cd565856b220f56d925c5c Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Fri, 17 Sep 2021 15:16:20 -0700 Subject: [PATCH 05/13] bug fix --- .../plugins/IntercomDestination.kt | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index 1e86a029..84061dd6 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -9,6 +9,7 @@ import com.segment.analytics.kotlin.core.platform.plugins.log import com.segment.analytics.kotlin.core.utilities.getString import com.segment.analytics.kotlin.core.utilities.putAll import com.segment.analytics.kotlin.core.utilities.safeJsonObject +import com.segment.analytics.kotlin.core.utilities.toContent import io.intercom.android.sdk.Company import io.intercom.android.sdk.Intercom import io.intercom.android.sdk.UserAttributes @@ -119,20 +120,21 @@ class IntercomDestination( analytics.log("Intercom.client().registerIdentifiedUser(registration)") } - payload.integrations["Intercom"]?.safeJsonObject?.let { intercomOptions -> - intercomOptions["userHash"]?.let { - val str = it.toString() + val intercomOptions = payload.integrations["Intercom"]?.safeJsonObject + intercomOptions?.let { it -> + it["userHash"]?.let { userHash -> + val str = userHash.toString() if (str.isNotEmpty()) { intercom.setUserHash(str) } } + } - if (!payload.traits.isNullOrEmpty() && intercomOptions.isNotEmpty()) { - setUserAttributes(payload.traits, intercomOptions) - } - else { - setUserAttributes(payload.traits, null) - } + if (!payload.traits.isNullOrEmpty() && !intercomOptions.isNullOrEmpty()) { + setUserAttributes(payload.traits, intercomOptions) + } + else { + setUserAttributes(payload.traits, null) } return result @@ -194,7 +196,7 @@ class IntercomDestination( traits.forEach { if (it.value is JsonPrimitive && it.key !in arrayOf(NAME, EMAIL, PHONE, "userId", "anonymousId")) { - builder.withCustomAttribute(it.key, it.value) + builder.withCustomAttribute(it.key, it.value.toContent()) } } @@ -231,7 +233,7 @@ class IntercomDestination( traits.forEach { if (it.value is JsonPrimitive && it.key !in arrayOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN)) { - builder.withCustomAttribute(it.key, it.value) + builder.withCustomAttribute(it.key, it.value.toContent()) } } From 1032b203e86e7ee46f2f5d5f63e21e8b6b7f8aae Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Mon, 20 Sep 2021 12:55:35 -0700 Subject: [PATCH 06/13] bug fix --- .../plugins/IntercomDestination.kt | 105 ++++++------------ 1 file changed, 34 insertions(+), 71 deletions(-) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index 84061dd6..89427506 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -6,10 +6,7 @@ import com.segment.analytics.kotlin.core.* import com.segment.analytics.kotlin.core.platform.DestinationPlugin import com.segment.analytics.kotlin.core.platform.Plugin import com.segment.analytics.kotlin.core.platform.plugins.log -import com.segment.analytics.kotlin.core.utilities.getString -import com.segment.analytics.kotlin.core.utilities.putAll -import com.segment.analytics.kotlin.core.utilities.safeJsonObject -import com.segment.analytics.kotlin.core.utilities.toContent +import com.segment.analytics.kotlin.core.utilities.* import io.intercom.android.sdk.Company import io.intercom.android.sdk.Intercom import io.intercom.android.sdk.UserAttributes @@ -70,15 +67,13 @@ class IntercomDestination( if (!properties.isNullOrEmpty()) { val price = buildJsonObject{ - val amount = properties[REVENUE] ?: properties[TOTAL] + val amount = properties.getDouble(REVENUE) ?: properties.getDouble(TOTAL) amount?.let { - if (it is Number) { - put(AMOUNT, it.toDouble() * 100) - } + put(AMOUNT, it * 100) } - properties[CURRENCY]?.let { - put(CURRENCY, it.toString()) + properties.getString(CURRENCY)?.let { + put(CURRENCY, it) } } @@ -87,10 +82,10 @@ class IntercomDestination( put(PRICE, price) } - properties.forEach { - if (it.key !in arrayOf("products", REVENUE, TOTAL, CURRENCY) - && it.value is JsonPrimitive) { - put(it.key, it.value) + properties.forEach { (key, value) -> + if (key !in arrayOf("products", REVENUE, TOTAL, CURRENCY) + && value is JsonPrimitive) { + put(key, value) } } } @@ -121,13 +116,8 @@ class IntercomDestination( } val intercomOptions = payload.integrations["Intercom"]?.safeJsonObject - intercomOptions?.let { it -> - it["userHash"]?.let { userHash -> - val str = userHash.toString() - if (str.isNotEmpty()) { - intercom.setUserHash(str) - } - } + intercomOptions?.getString("userHash")?.let { + intercom.setUserHash(it) } if (!payload.traits.isNullOrEmpty() && !intercomOptions.isNullOrEmpty()) { @@ -166,37 +156,25 @@ class IntercomDestination( private fun setUserAttributes(traits: Traits, intercomOptions: JsonObject?) { val builder = UserAttributes.Builder() - .withName(traits[NAME].toString()) - .withEmail(traits[EMAIL].toString()) - .withPhone(traits[PHONE].toString()) + .withName(traits.getString(NAME)) + .withEmail(traits.getString(EMAIL)) + .withPhone(traits.getString(PHONE)) intercomOptions?.let { - builder.withLanguageOverride(it[LANGUAGE_OVERRIDE].toString()) - - it[CREATED_AT]?.let { createdAt -> - if (createdAt is Number) { - builder.withSignedUpAt(createdAt.toLong()) - } - } - - it[UNSUBSCRIBED_FROM_EMAILS]?.let { unsubscribed -> - if (unsubscribed is JsonPrimitive) { - builder.withUnsubscribedFromEmails(unsubscribed.jsonPrimitive.booleanOrNull) - } - } + builder.withLanguageOverride(it.getString(LANGUAGE_OVERRIDE)) + builder.withSignedUpAt(it.getLong(CREATED_AT)) + builder.withUnsubscribedFromEmails(it.getBoolean(UNSUBSCRIBED_FROM_EMAILS)) } - traits[COMPANY]?.let { - if (it is JsonObject) { - val company = setCompany(it) - builder.withCompany(company) - } + traits[COMPANY]?.safeJsonObject?.let { + val company = setCompany(it) + builder.withCompany(company) } - traits.forEach { - if (it.value is JsonPrimitive && - it.key !in arrayOf(NAME, EMAIL, PHONE, "userId", "anonymousId")) { - builder.withCustomAttribute(it.key, it.value.toContent()) + traits.forEach { (key, value) -> + if (value is JsonPrimitive && + key !in arrayOf(NAME, EMAIL, PHONE, "userId", "anonymousId")) { + builder.withCustomAttribute(key, value.toContent()) } } @@ -206,34 +184,19 @@ class IntercomDestination( private fun setCompany(traits: JsonObject): Company { val builder = Company.Builder() - traits["id"]?.let { - builder.withCompanyId(it.toString()) + traits.getString("id")?.let { + builder.withCompanyId(it) } ?: return builder.build() - traits[NAME]?.let { - builder.withName(it.toString()) - } - - traits[CREATED_AT]?.let { - if (it is JsonPrimitive) { - builder.withCreatedAt(it.jsonPrimitive.longOrNull) - } - } - - traits[MONTHLY_SPEND]?.let { - if (it is Number) { - builder.withMonthlySpend(it.toInt()) - } - } - - traits[PLAN]?.let { - builder.withPlan(it.toString()) - } + builder.withName(traits.getString(NAME)) + builder.withCreatedAt(traits.getLong(CREATED_AT)) + builder.withMonthlySpend(traits.getInt(MONTHLY_SPEND)) + builder.withPlan(traits.getString(PLAN)) - traits.forEach { - if (it.value is JsonPrimitive && - it.key !in arrayOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN)) { - builder.withCustomAttribute(it.key, it.value.toContent()) + traits.forEach { (key, value) -> + if (value is JsonPrimitive && + key !in arrayOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN)) { + builder.withCustomAttribute(key, value.toContent()) } } From e8058bd78907a268ab3a5d280fd3a9346d114622 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Mon, 20 Sep 2021 12:56:30 -0700 Subject: [PATCH 07/13] add usage to application --- .../com/segment/analytics/destinations/MainApplication.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/MainApplication.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/MainApplication.kt index 1159dbd2..b3e3955a 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/MainApplication.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/MainApplication.kt @@ -1,10 +1,7 @@ package com.segment.analytics.destinations import android.app.Application -import com.segment.analytics.destinations.plugins.AmplitudeSession -import com.segment.analytics.destinations.plugins.FirebaseDestination -import com.segment.analytics.destinations.plugins.MixpanelDestination -import com.segment.analytics.destinations.plugins.WebhookPlugin +import com.segment.analytics.destinations.plugins.* import com.segment.analytics.kotlin.android.Analytics import com.segment.analytics.kotlin.core.Analytics import java.util.concurrent.Executors @@ -40,5 +37,8 @@ class MainApplication : Application() { // Try out Firebase Destination analytics.add(FirebaseDestination(applicationContext)) + + // Try out Intercom destination + analytics.add(IntercomDestination(this)) } } From 73ac4e5af774d3891db347811676be10a197510a Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Mon, 20 Sep 2021 13:00:51 -0700 Subject: [PATCH 08/13] bump up sample destination project minSdkVersion to 21 --- samples/kotlin-android-app-destinations/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/kotlin-android-app-destinations/build.gradle b/samples/kotlin-android-app-destinations/build.gradle index 5925edfc..8e091475 100644 --- a/samples/kotlin-android-app-destinations/build.gradle +++ b/samples/kotlin-android-app-destinations/build.gradle @@ -11,7 +11,7 @@ android { defaultConfig { multiDexEnabled true applicationId "com.segment.analytics.destinations" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 30 versionCode 3 versionName "2.0" From e52bd6aa0d06558b8c4684a4bf2231922df949f3 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Wed, 22 Sep 2021 11:06:39 -0700 Subject: [PATCH 09/13] add unit tests --- .../build.gradle | 9 + .../plugins/IntercomDestinationTest.kt | 362 ++++++++++++++++++ 2 files changed, 371 insertions(+) create mode 100644 samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt diff --git a/samples/kotlin-android-app-destinations/build.gradle b/samples/kotlin-android-app-destinations/build.gradle index 8e091475..dad8c2df 100644 --- a/samples/kotlin-android-app-destinations/build.gradle +++ b/samples/kotlin-android-app-destinations/build.gradle @@ -34,6 +34,11 @@ android { kotlinOptions { jvmTarget = '1.8' } + testOptions { + unitTests.all { + useJUnitPlatform() + } + } } dependencies { @@ -67,7 +72,11 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-process:2.3.1' implementation 'androidx.lifecycle:lifecycle-common-java8:2.3.1' + testImplementation 'io.mockk:mockk:1.10.6' testImplementation 'junit:junit:4.+' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2' + testImplementation platform("org.junit:junit-bom:5.7.2") + testImplementation "org.junit.jupiter:junit-jupiter" androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } \ No newline at end of file diff --git a/samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt b/samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt new file mode 100644 index 00000000..9df82ecc --- /dev/null +++ b/samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt @@ -0,0 +1,362 @@ +package com.segment.analytics.destinations.plugins + +import android.app.Application +import android.app.TaskStackBuilder +import android.util.Log +import com.segment.analytics.kotlin.core.* +import com.segment.analytics.kotlin.core.platform.Plugin +import com.segment.analytics.kotlin.core.utilities.getString +import io.intercom.android.sdk.Company +import io.intercom.android.sdk.Intercom +import io.intercom.android.sdk.UnreadConversationCountListener +import io.intercom.android.sdk.UserAttributes +import io.intercom.android.sdk.helpcenter.api.CollectionContentRequestCallback +import io.intercom.android.sdk.helpcenter.api.CollectionRequestCallback +import io.intercom.android.sdk.helpcenter.api.SearchRequestCallback +import io.intercom.android.sdk.identity.Registration +import io.mockk.* +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.serialization.json.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.io.ByteArrayInputStream +import java.net.HttpURLConnection + +internal class IntercomDestinationTest { + + @MockK(relaxUnitFun = true) + lateinit var application: Application + + private lateinit var intercom: Intercom + + private var configuration: Configuration + + private lateinit var analytics: Analytics + + private lateinit var intercomDestination: IntercomDestination + + private val testDispatcher = TestCoroutineDispatcher() + + private val testScope = TestCoroutineScope(testDispatcher) + + init { + MockKAnnotations.init(this) + + // mock intercom + mockkStatic(Intercom::class) + intercom = spyk(StubIntercom()) + every { Intercom.client() } returns intercom + every { Intercom.initialize(any(), any(), any()) } just Runs + + // mock configuration + configuration = spyk(Configuration("123", application)) + every { configuration getProperty "ioDispatcher" } propertyType CoroutineDispatcher::class returns testDispatcher + every { configuration getProperty "analyticsScope"} propertyType CoroutineScope::class returns testScope + + // mock java log + mockkStatic(Log::class) + every { Log.println(any(), any(), any()) } returns 0 + every { Log.v(any(), any()) } returns 0 + every { Log.d(any(), any()) } returns 0 + every { Log.i(any(), any()) } returns 0 + every { Log.e(any(), any()) } returns 0 + + // mock http client + mockkConstructor(HTTPClient::class) + val settingsStream = ByteArrayInputStream( + """ + {"integrations":{"Intercom":{"appId":"qe2y1u8q","collectContext":false,"mobileApiKey":"android_sdk-4c2bc22f45f0f20629d4a70c3bb803845039800b"}},"plan":{},"edgeFunction":{}} + """.trimIndent().toByteArray() + ) + val httpConnection: HttpURLConnection = mockk() + val connection = object : Connection(httpConnection, settingsStream, null) {} + every { anyConstructed().settings(any()) } returns connection + } + + @BeforeEach + internal fun setUp() { + analytics = Analytics(configuration) + intercomDestination = spyk(IntercomDestination(application)) + } + + @Test + fun `intercom client initialized when settings is updated`() { + val settings = slot() + + analytics.add(intercomDestination) + + verify { + intercomDestination.update(capture(settings), Plugin.UpdateType.Initial) + Intercom.initialize(any(), any(), any()) + Intercom.client() + } + assertEquals(intercom, intercomDestination.intercom) + with(settings.captured) { + val integration = this.integrations[intercomDestination.key]?.jsonObject + assertNotNull(integration) + assertEquals("android_sdk-4c2bc22f45f0f20629d4a70c3bb803845039800b", integration?.getString("mobileApiKey")) + assertEquals("qe2y1u8q", integration?.getString("appId")) + } + } + + @Test + fun `intercom client not re-initialized when settings is fresh`() { + analytics.add(intercomDestination) + analytics.checkSettings() + verify (exactly = 1) { + intercomDestination.update(any(), Plugin.UpdateType.Initial) + Intercom.initialize(any(), any(), any()) + Intercom.client() + } + } + + @Test + fun `track when all fields are presented`() { + val eventName = slot() + val event = slot() + val expected = buildJsonObject { + putJsonObject("price") { + put("amount", 100.0) + put("currency", "USD") + } + put("others", "other") + } + + analytics.add(intercomDestination) + analytics.track("track", buildJsonObject { + put("revenue", 1) + put("total", 2) + put("currency", "USD") + put("products", "products") + put("others", "other") + }) + + verify (timeout = 1000) { intercom.logEvent(capture(eventName), capture(event)) } + assertEquals("track", eventName.captured) + assertEquals(expected, event.captured) + } + + @Test + fun `track when all fields except total are absent`() { + val eventName = slot() + val event = slot() + val expected = buildJsonObject { + putJsonObject("price") { + put("amount", 200.0) + } + } + + analytics.add(intercomDestination) + analytics.track("track", buildJsonObject { + put("total", 2) + }) + + verify (timeout = 1000) { intercom.logEvent(capture(eventName), capture(event)) } + assertEquals("track", eventName.captured) + assertEquals(expected, event.captured) + } + + @Test + fun `identify with empty user id and empty traits`() { + val attributes = slot() + val expected = UserAttributes.Builder().build() + + analytics.add(intercomDestination) + analytics.identify("") + + verify (timeout = 1000) { intercom.registerUnidentifiedUser() } + verify (timeout = 1000) { intercom.updateUser(capture(attributes)) } + assertEquals(expected, attributes.captured) + } + + @Test + fun `identify with user id and traits`() { + mockkStatic(android.text.TextUtils::class) + every { android.text.TextUtils.isEmpty(any()) } returns false + + val registration = slot() + val attributes = slot() + val expectedRegistration = Registration.create().withUserId("test") + val expectedAttributes = UserAttributes.Builder() + .withName("kotlin") + .withEmail("kotlin@test.com") + .withPhone("1234567890") + .withCustomAttribute("other", "other") + .withCompany(Company.Builder() + .withCompanyId("company123") + .withName("kotlin company") + .withCreatedAt(9876543210987L) + .withMonthlySpend(123456) + .withPlan("abc") + .withCustomAttribute("other", "other") + .build() + ) + .build() + val payload = buildJsonObject { + put("name", "kotlin") + put("email", "kotlin@test.com") + put("phone", "1234567890") + put("other", "other") + putJsonObject("company") { + put("id", "company123") + put("name", "kotlin company") + put("monthlySpend", 123456) + put("plan", "abc") + put("other", "other") + put("createdAt", 9876543210987L) + } + } + + analytics.add(intercomDestination) + analytics.identify("test", payload) + + verify (timeout = 1000) { intercom.registerIdentifiedUser(capture(registration)) } + verify (timeout = 1000) { intercom.updateUser(capture(attributes)) } + assertEquals(expectedRegistration, registration.captured) + assertEquals(expectedAttributes, attributes.captured) + } + + @Test + fun `group with group id`() { + val attributes = slot() + val expected = UserAttributes.Builder() + .withCompany(Company.Builder() + .withCompanyId("company123") + .build() + ) + .build() + + analytics.add(intercomDestination) + analytics.group("company123") + + verify (timeout = 1000) { intercom.updateUser(capture(attributes)) } + assertEquals(expected, attributes.captured) + } + + @Test + fun `group with group id and traits`() { + val attributes = slot() + val expected = UserAttributes.Builder() + .withCompany(Company.Builder() + .withCompanyId("company123") + .withName("kotlin company") + .withCreatedAt(9876543210987L) + .withMonthlySpend(123456) + .withPlan("abc") + .withCustomAttribute("other", "other") + .build() + ) + .build() + + analytics.add(intercomDestination) + analytics.group("company123", buildJsonObject { + put("name", "kotlin company") + put("monthlySpend", 123456) + put("plan", "abc") + put("other", "other") + put("createdAt", 9876543210987L) + }) + + verify (timeout = 1000) { intercom.updateUser(capture(attributes)) } + assertEquals(expected, attributes.captured) + } + + @Test + fun reset() { + analytics.add(intercomDestination) + intercomDestination.reset() + verify { intercom.logout() } + } + + internal class StubIntercom: Intercom() { + override fun registerUnidentifiedUser() { + } + + override fun registerIdentifiedUser(p0: Registration?) { + } + + override fun setUserHash(p0: String?) { + } + + override fun updateUser(p0: UserAttributes?) { + } + + override fun logEvent(p0: String?) { + } + + override fun logEvent(p0: String?, p1: MutableMap?) { + } + + override fun displayMessenger() { + } + + override fun displayMessageComposer() { + } + + override fun displayMessageComposer(p0: String?) { + } + + override fun displayConversationsList() { + } + + override fun displayHelpCenter() { + } + + override fun displayHelpCenterCollections(p0: MutableList?) { + } + + override fun displayCarousel(p0: String?) { + } + + override fun setBottomPadding(p0: Int) { + } + + override fun setInAppMessageVisibility(p0: Visibility?) { + } + + override fun setLauncherVisibility(p0: Visibility?) { + } + + override fun hideIntercom() { + } + + override fun handlePushMessage() { + } + + override fun handlePushMessage(p0: TaskStackBuilder?) { + } + + override fun reset() { + } + + override fun logout() { + } + + override fun getUnreadConversationCount(): Int = 0 + + override fun addUnreadConversationCountListener(p0: UnreadConversationCountListener) { + } + + override fun removeUnreadConversationCountListener(p0: UnreadConversationCountListener?) { + } + + override fun displayArticle(p0: String) { + } + + override fun fetchHelpCenterCollections(p0: CollectionRequestCallback?) { + } + + override fun fetchHelpCenterCollection(p0: String?, p1: CollectionContentRequestCallback?) { + } + + override fun searchHelpCenter(p0: String?, p1: SearchRequestCallback?) { + } + + } +} \ No newline at end of file From 598151e1fdcb4b31189f1216401818c555ed60dc Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Wed, 22 Sep 2021 11:06:53 -0700 Subject: [PATCH 10/13] bug fix --- .../plugins/IntercomDestination.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index 89427506..939bf860 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -83,7 +83,7 @@ class IntercomDestination( } properties.forEach { (key, value) -> - if (key !in arrayOf("products", REVENUE, TOTAL, CURRENCY) + if (key !in setOf("products", REVENUE, TOTAL, CURRENCY) && value is JsonPrimitive) { put(key, value) } @@ -156,9 +156,10 @@ class IntercomDestination( private fun setUserAttributes(traits: Traits, intercomOptions: JsonObject?) { val builder = UserAttributes.Builder() - .withName(traits.getString(NAME)) - .withEmail(traits.getString(EMAIL)) - .withPhone(traits.getString(PHONE)) + + traits.getString(NAME)?.let { builder.withName(it) } + traits.getString(EMAIL)?.let { builder.withEmail(it) } + traits.getString(PHONE)?.let { builder.withPhone(it) } intercomOptions?.let { builder.withLanguageOverride(it.getString(LANGUAGE_OVERRIDE)) @@ -173,7 +174,7 @@ class IntercomDestination( traits.forEach { (key, value) -> if (value is JsonPrimitive && - key !in arrayOf(NAME, EMAIL, PHONE, "userId", "anonymousId")) { + key !in setOf(NAME, EMAIL, PHONE, "userId", "anonymousId")) { builder.withCustomAttribute(key, value.toContent()) } } @@ -188,14 +189,15 @@ class IntercomDestination( builder.withCompanyId(it) } ?: return builder.build() - builder.withName(traits.getString(NAME)) - builder.withCreatedAt(traits.getLong(CREATED_AT)) - builder.withMonthlySpend(traits.getInt(MONTHLY_SPEND)) - builder.withPlan(traits.getString(PLAN)) + traits.getString(NAME)?.let { builder.withName(it) } + traits.getLong(CREATED_AT)?.let { builder.withCreatedAt(it) } + traits.getInt(MONTHLY_SPEND)?.let { builder.withMonthlySpend(it) } + traits.getString(PLAN)?.let { builder.withPlan(it) } traits.forEach { (key, value) -> if (value is JsonPrimitive && - key !in arrayOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN)) { + key !in setOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN) + ) { builder.withCustomAttribute(key, value.toContent()) } } From 8d290b7700cc1cb19e318ac885b2a752f465636e Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Wed, 22 Sep 2021 11:11:58 -0700 Subject: [PATCH 11/13] add comments and license --- .../plugins/IntercomDestination.kt | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index 939bf860..6cf6a0dd 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -13,6 +13,50 @@ import io.intercom.android.sdk.UserAttributes import io.intercom.android.sdk.identity.Registration import kotlinx.serialization.json.* +/* +This is an example of the Intercom device-mode destination plugin that can be integrated with +Segment analytics. +Note: This plugin is NOT SUPPORTED by Segment. It is here merely as an example, +and for your convenience should you find it useful. +# Instructions for adding Intercom: +- In your app-module build.gradle file add the following: +``` +... +dependencies { + ... + // Intercom + implementation 'io.intercom.android:intercom-sdk-base:10.1.1' + implementation 'io.intercom.android:intercom-sdk-fcm:10.1.1' +} +``` +- Copy this entire IntercomDestination.kt file into your project's codebase. +- Go to your project's codebase and wherever u initialize the analytics client add these lines +``` +val intercom = IntercomDestination() +analytics.add(intercom) +``` + +Note: due to the inclusion of Intercom partner integration your minSdk cannot be smaller than 21 + +MIT License +Copyright (c) 2021 Segment +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + class IntercomDestination( private val application: Application ): DestinationPlugin(), AndroidLifecycle { From 302c65fa0276889aca5020a193b7427ec52d0cf2 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Wed, 22 Sep 2021 12:47:21 -0700 Subject: [PATCH 12/13] address comments --- .../plugins/IntercomDestination.kt | 61 ++++++------ .../plugins/IntercomDestinationTest.kt | 95 +------------------ 2 files changed, 34 insertions(+), 122 deletions(-) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index 6cf6a0dd..ca968974 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -67,28 +67,6 @@ class IntercomDestination( lateinit var intercom: Intercom private set - // Intercom common specced attributes - private val NAME = "name" - private val CREATED_AT = "createdAt" - private val COMPANY = "company" - private val PRICE = "price" - private val AMOUNT = "amount" - private val CURRENCY = "currency" - - // Intercom specced user attributes - private val EMAIL = "email" - private val PHONE = "phone" - private val LANGUAGE_OVERRIDE = "languageOverride" - private val UNSUBSCRIBED_FROM_EMAILS = "unsubscribedFromEmails" - - // Intercom specced group attributes - private val MONTHLY_SPEND = "monthlySpend" - private val PLAN = "plan" - - // Segment specced properties - private val REVENUE = "revenue" - private val TOTAL = "total" - override fun update(settings: Settings, type: Plugin.UpdateType) { // if we've already set up this singleton SDK, can't do it again, so skip. if (type != Plugin.UpdateType.Initial) return @@ -227,18 +205,18 @@ class IntercomDestination( analytics.log("Intercom.client().updateUser(userAttributes)") } - private fun setCompany(traits: JsonObject): Company { + private fun setCompany(company: JsonObject): Company { val builder = Company.Builder() - traits.getString("id")?.let { + company.getString("id")?.let { builder.withCompanyId(it) } ?: return builder.build() - traits.getString(NAME)?.let { builder.withName(it) } - traits.getLong(CREATED_AT)?.let { builder.withCreatedAt(it) } - traits.getInt(MONTHLY_SPEND)?.let { builder.withMonthlySpend(it) } - traits.getString(PLAN)?.let { builder.withPlan(it) } + company.getString(NAME)?.let { builder.withName(it) } + company.getLong(CREATED_AT)?.let { builder.withCreatedAt(it) } + company.getInt(MONTHLY_SPEND)?.let { builder.withMonthlySpend(it) } + company.getString(PLAN)?.let { builder.withPlan(it) } - traits.forEach { (key, value) -> + company.forEach { (key, value) -> if (value is JsonPrimitive && key !in setOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN) ) { @@ -248,4 +226,29 @@ class IntercomDestination( return builder.build() } + + companion object { + + // Intercom common specced attributes + private const val NAME = "name" + private const val CREATED_AT = "createdAt" + private const val COMPANY = "company" + private const val PRICE = "price" + private const val AMOUNT = "amount" + private const val CURRENCY = "currency" + + // Intercom specced user attributes + private const val EMAIL = "email" + private const val PHONE = "phone" + private const val LANGUAGE_OVERRIDE = "languageOverride" + private const val UNSUBSCRIBED_FROM_EMAILS = "unsubscribedFromEmails" + + // Intercom specced group attributes + private const val MONTHLY_SPEND = "monthlySpend" + private const val PLAN = "plan" + + // Segment specced properties + private const val REVENUE = "revenue" + private const val TOTAL = "total" + } } \ No newline at end of file diff --git a/samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt b/samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt index 9df82ecc..f613e24b 100644 --- a/samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt +++ b/samples/kotlin-android-app-destinations/src/test/java/com/segment/analytics/destinations/plugins/IntercomDestinationTest.kt @@ -1,18 +1,13 @@ package com.segment.analytics.destinations.plugins import android.app.Application -import android.app.TaskStackBuilder import android.util.Log import com.segment.analytics.kotlin.core.* import com.segment.analytics.kotlin.core.platform.Plugin import com.segment.analytics.kotlin.core.utilities.getString import io.intercom.android.sdk.Company import io.intercom.android.sdk.Intercom -import io.intercom.android.sdk.UnreadConversationCountListener import io.intercom.android.sdk.UserAttributes -import io.intercom.android.sdk.helpcenter.api.CollectionContentRequestCallback -import io.intercom.android.sdk.helpcenter.api.CollectionRequestCallback -import io.intercom.android.sdk.helpcenter.api.SearchRequestCallback import io.intercom.android.sdk.identity.Registration import io.mockk.* import io.mockk.impl.annotations.MockK @@ -33,7 +28,8 @@ internal class IntercomDestinationTest { @MockK(relaxUnitFun = true) lateinit var application: Application - private lateinit var intercom: Intercom + @MockK(relaxUnitFun = true) + lateinit var intercom: Intercom private var configuration: Configuration @@ -50,7 +46,6 @@ internal class IntercomDestinationTest { // mock intercom mockkStatic(Intercom::class) - intercom = spyk(StubIntercom()) every { Intercom.client() } returns intercom every { Intercom.initialize(any(), any(), any()) } just Runs @@ -273,90 +268,4 @@ internal class IntercomDestinationTest { intercomDestination.reset() verify { intercom.logout() } } - - internal class StubIntercom: Intercom() { - override fun registerUnidentifiedUser() { - } - - override fun registerIdentifiedUser(p0: Registration?) { - } - - override fun setUserHash(p0: String?) { - } - - override fun updateUser(p0: UserAttributes?) { - } - - override fun logEvent(p0: String?) { - } - - override fun logEvent(p0: String?, p1: MutableMap?) { - } - - override fun displayMessenger() { - } - - override fun displayMessageComposer() { - } - - override fun displayMessageComposer(p0: String?) { - } - - override fun displayConversationsList() { - } - - override fun displayHelpCenter() { - } - - override fun displayHelpCenterCollections(p0: MutableList?) { - } - - override fun displayCarousel(p0: String?) { - } - - override fun setBottomPadding(p0: Int) { - } - - override fun setInAppMessageVisibility(p0: Visibility?) { - } - - override fun setLauncherVisibility(p0: Visibility?) { - } - - override fun hideIntercom() { - } - - override fun handlePushMessage() { - } - - override fun handlePushMessage(p0: TaskStackBuilder?) { - } - - override fun reset() { - } - - override fun logout() { - } - - override fun getUnreadConversationCount(): Int = 0 - - override fun addUnreadConversationCountListener(p0: UnreadConversationCountListener) { - } - - override fun removeUnreadConversationCountListener(p0: UnreadConversationCountListener?) { - } - - override fun displayArticle(p0: String) { - } - - override fun fetchHelpCenterCollections(p0: CollectionRequestCallback?) { - } - - override fun fetchHelpCenterCollection(p0: String?, p1: CollectionContentRequestCallback?) { - } - - override fun searchHelpCenter(p0: String?, p1: SearchRequestCallback?) { - } - - } } \ No newline at end of file From 293ed4950c3bfc6bfd026011708ad7949b1fbcb5 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Wed, 22 Sep 2021 14:07:55 -0700 Subject: [PATCH 13/13] address comments again --- .../destinations/plugins/IntercomDestination.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt index ca968974..3029a98d 100644 --- a/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt +++ b/samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/plugins/IntercomDestination.kt @@ -99,12 +99,13 @@ class IntercomDestination( } } - val event = buildJsonObject { + val eventProperties = buildJsonObject { if (!price.isNullOrEmpty()) { put(PRICE, price) } properties.forEach { (key, value) -> + // here we are only interested in primitive values and not maps or collections if (key !in setOf("products", REVENUE, TOTAL, CURRENCY) && value is JsonPrimitive) { put(key, value) @@ -112,8 +113,8 @@ class IntercomDestination( } } - intercom.logEvent(eventName, event) - analytics.log("Intercom.client().logEvent($eventName, $event)") + intercom.logEvent(eventName, eventProperties) + analytics.log("Intercom.client().logEvent($eventName, $eventProperties)") } else { intercom.logEvent(eventName) @@ -195,6 +196,7 @@ class IntercomDestination( } traits.forEach { (key, value) -> + // here we are only interested in primitive values and not maps or collections if (value is JsonPrimitive && key !in setOf(NAME, EMAIL, PHONE, "userId", "anonymousId")) { builder.withCustomAttribute(key, value.toContent()) @@ -217,6 +219,7 @@ class IntercomDestination( company.getString(PLAN)?.let { builder.withPlan(it) } company.forEach { (key, value) -> + // here we are only interested in primitive values and not maps or collections if (value is JsonPrimitive && key !in setOf("id", NAME, CREATED_AT, MONTHLY_SPEND, PLAN) ) {