Skip to content

Commit 1b701e8

Browse files
authored
Mixpanel Destination (#9)
* add json utilities and MixpanelDestination.kt * fix from rebase * remove adding token from main api * fix tests
1 parent 185275b commit 1b701e8

File tree

7 files changed

+301
-28
lines changed

7 files changed

+301
-28
lines changed

analytics-kotlin/src/main/java/com/segment/analytics/Analytics.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ import sovran.kotlin.Store
1818
import sovran.kotlin.Subscriber
1919

2020
class Analytics(internal val configuration: Configuration) : Subscriber {
21-
internal val store: Store
21+
22+
private val _store: Store
23+
internal val store: Store get() {
24+
return _store
25+
}
26+
2227
internal val timeline: Timeline
2328
internal val analyticsScope: CoroutineScope
2429
internal val storage: Storage
@@ -33,7 +38,7 @@ class Analytics(internal val configuration: Configuration) : Subscriber {
3338
processingDispatcher = configuration.analyticsDispatcher
3439
ioDispatcher = configuration.ioDispatcher
3540
timeline = Timeline().also { it.analytics = this }
36-
store = Store()
41+
_store = Store()
3742

3843
storage = configuration.storageProvider.getStorage(
3944
analytics = this,
@@ -359,10 +364,6 @@ class Analytics(internal val configuration: Configuration) : Subscriber {
359364
fun add(plugin: Plugin): Analytics {
360365
// could be done in background thread
361366
this.timeline.add(plugin)
362-
store.currentState(System::class)?.settings?.let {
363-
// if we have settings then update plugin with it
364-
plugin.update(it)
365-
}
366367
if (plugin is DestinationPlugin && plugin.name != "Segment.io") {
367368
analyticsScope.launch(ioDispatcher) {
368369
store.dispatch(System.AddIntegrationAction(plugin.name), System::class)

analytics-kotlin/src/main/java/com/segment/analytics/platform/Timeline.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.segment.analytics.platform
22

33
import com.segment.analytics.Analytics
44
import com.segment.analytics.BaseEvent
5+
import com.segment.analytics.System
56

67
// Platform abstraction for managing all plugins and their execution
78
// Currently the execution follows
@@ -56,8 +57,12 @@ internal class Timeline {
5657

5758
// Register a new plugin
5859
fun add(plugin: Plugin) {
59-
plugins[plugin.type]?.add(plugin)
6060
plugin.setup(analytics)
61+
plugins[plugin.type]?.add(plugin)
62+
analytics.store.currentState(System::class)?.settings?.let {
63+
// if we have settings then update plugin with it
64+
plugin.update(it)
65+
}
6166
}
6267

6368
// Remove a registered plugin
Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,88 @@
11
package com.segment.analytics.utilities
22

3-
import kotlinx.serialization.json.JsonElement
4-
import kotlinx.serialization.json.JsonObject
5-
import kotlinx.serialization.json.JsonObjectBuilder
6-
import kotlinx.serialization.json.put
3+
import kotlinx.serialization.json.*
4+
import org.json.JSONArray
5+
import org.json.JSONObject
76

7+
// Utility function to put all values of `obj` into current builder
88
fun JsonObjectBuilder.putAll(obj: JsonObject) {
99
obj.forEach { (key, value) ->
1010
put(key, value)
1111
}
1212
}
1313

14+
// Utility function to put "undefined" value in-stead of `null` when building JsonObject
1415
fun JsonObjectBuilder.putUndefinedIfNull(key: String, value: CharSequence?): JsonElement? =
1516
if (value.isNullOrEmpty()) {
1617
put(key, "undefined")
1718
} else {
1819
put(key, value.toString())
1920
}
21+
22+
// Transform kotlinx.JsonArray to org.json.JSONArray
23+
fun List<JsonElement>.toJSONArray(): JSONArray {
24+
val constructed = JSONArray()
25+
for (item in this) {
26+
when (item) {
27+
is JsonPrimitive -> {
28+
constructed.put(item.toContent())
29+
}
30+
is JsonObject -> {
31+
constructed.put(item.toJSONObject())
32+
}
33+
is JsonArray -> {
34+
constructed.put(item.toJSONArray())
35+
}
36+
}
37+
}
38+
return constructed
39+
}
40+
41+
// Transform kotlinx.JsonArray to org.json.JSONArray
42+
fun Map<String, JsonElement>.toJSONObject(): JSONObject {
43+
val constructed = JSONObject()
44+
for ((k, v) in entries) {
45+
when (v) {
46+
is JsonPrimitive -> {
47+
constructed.put(k, v.toContent())
48+
}
49+
is JsonObject -> {
50+
constructed.put(k, v.toJSONObject())
51+
}
52+
is JsonArray -> {
53+
constructed.put(k, v.toJSONArray())
54+
}
55+
}
56+
}
57+
return constructed
58+
}
59+
60+
// Utility function to convert a jsonPrimitive to its appropriate kotlin type
61+
fun JsonPrimitive.toContent(): Any? {
62+
this.booleanOrNull?.let {
63+
return it
64+
}
65+
this.intOrNull?.let {
66+
return it
67+
}
68+
this.longOrNull?.let {
69+
return it
70+
}
71+
this.floatOrNull?.let {
72+
return it
73+
}
74+
this.doubleOrNull?.let {
75+
return it
76+
}
77+
return contentOrNull
78+
}
79+
80+
// Utility function to retrieve a boolean value from a jsonObject
81+
fun JsonObject.getBoolean(key: String): Boolean? = this[key]?.jsonPrimitive?.boolean
82+
83+
// Utility function to retrieve a string value from a jsonObject
84+
fun JsonObject.getString(key: String): String? = this[key]?.jsonPrimitive?.contentOrNull
85+
86+
// Utility function to retrieve a string set (from jsonArray) from a jsonObject
87+
fun JsonObject.getStringSet(key: String): Set<String>? =
88+
this[key]?.jsonArray?.map { it.jsonPrimitive.content }?.toSet()

analytics-kotlin/src/test/java/com/segment/analytics/main/utils/Mocks.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ fun mockContext(): Context {
3434

3535
fun mockAnalytics(): Analytics {
3636
val mock = mockk<Analytics>(relaxed = true)
37+
val mockStore = Store()
38+
every { mock.store } returns mockStore
3739
return mock
3840
}
3941

samples/kotlin-android-app-destinations/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ android {
3737
}
3838

3939
dependencies {
40+
41+
// Mixpanel
42+
api 'com.mixpanel.android:mixpanel-android:5.8.7'
43+
44+
4045
implementation project(':analytics-kotlin')
4146

4247
implementation 'androidx.multidex:multidex:2.0.1'

samples/kotlin-android-app-destinations/src/main/java/com/segment/analytics/destinations/MainApplication.kt

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ package com.segment.analytics.destinations
33
import android.app.Application
44
import com.segment.analytics.*
55
import com.segment.analytics.destinations.plugins.AmplitudeSession
6+
import com.segment.analytics.destinations.plugins.MixpanelDestination
67
import com.segment.analytics.destinations.plugins.WebhookPlugin
7-
import com.segment.analytics.platform.Plugin
8-
import com.segment.analytics.utilities.*
98
import kotlinx.coroutines.CoroutineScope
109
import kotlinx.coroutines.Dispatchers
1110
import kotlinx.coroutines.SupervisorJob
@@ -29,21 +28,9 @@ class MainApplication : Application() {
2928
this.flushAt = 1
3029
this.flushInterval = 0
3130
}
32-
analytics.add(object : Plugin {
33-
override val type: Plugin.Type = Plugin.Type.Enrichment
34-
override val name: String = "Foo"
35-
override lateinit var analytics: Analytics
36-
37-
override fun execute(event: BaseEvent): BaseEvent? {
38-
event.enableIntegration("AppsFlyer")
39-
event.disableIntegration("AppBoy")
40-
event.putInContext("foo", "bar")
41-
event.putInContextUnderKey("device", "android", true)
42-
event.removeFromContext("locale")
43-
return event
44-
}
45-
46-
})
31+
32+
analytics.add(MixpanelDestination(applicationContext))
33+
4734
// A random webhook url to view your events
4835
analytics.add(
4936
WebhookPlugin(

0 commit comments

Comments
 (0)