Skip to content

Commit d3a1a99

Browse files
authored
Wenxi/convenience methods and misses for plugin authors (#60)
* add convenience methods for BaseEvent * add convenience methods for updating json object * add unit tests for updating json object * bug fix * add unit tests for EventManipulationFunctionsTest.kt
1 parent 12bf0cc commit d3a1a99

File tree

6 files changed

+305
-17
lines changed

6 files changed

+305
-17
lines changed

core/src/main/java/com/segment/analytics/kotlin/core/Events.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ sealed class BaseEvent {
7979
// the userId tied to the event
8080
abstract var userId: String
8181

82+
companion object {
83+
internal const val ALL_INTEGRATIONS_KEY = "All"
84+
}
85+
8286
internal fun applyBaseData() {
8387
this.timestamp = Instant.now().toString()
8488
this.context = emptyJsonObject

core/src/main/java/com/segment/analytics/kotlin/core/utilities/EventManipulationFunctions.kt

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,23 @@ import kotlinx.serialization.serializer
1010

1111
// Mark integration as enabled, for this event
1212
fun BaseEvent.enableIntegration(integrationName: String): BaseEvent {
13-
return putIntegrations(integrationName, true)
13+
// if not exist yet, enable it
14+
if (!integrations.containsKey(integrationName)) {
15+
return putIntegrations(integrationName, true)
16+
}
17+
18+
// if it's a boolean value
19+
integrations.getBoolean(integrationName)?.let { enabled ->
20+
// if it's not enabled, enable it
21+
// otherwise, do nothing
22+
if (!enabled) {
23+
return putIntegrations(integrationName, true)
24+
}
25+
}
26+
27+
// otherwise it's a dictionary already, it's considered enabled, so don't
28+
// overwrite whatever they may have put there.
29+
return this
1430
}
1531

1632
// Mark integration as disabled, for this event
@@ -97,5 +113,37 @@ inline fun <reified T : Any> BaseEvent.putInContextUnderKey(
97113

98114
fun BaseEvent.removeFromContext(key: String): BaseEvent {
99115
context = JsonObject(context.filterNot { (k, _) -> k == key })
116+
return this
117+
}
118+
119+
120+
fun BaseEvent.disableCloudIntegrations(exceptKeys: List<String>? = null): BaseEvent {
121+
integrations = buildJsonObject {
122+
put(BaseEvent.ALL_INTEGRATIONS_KEY, false)
123+
124+
exceptKeys?.forEach { key ->
125+
if (integrations.containsKey(key)) {
126+
if(integrations.getBoolean(key) != null) {
127+
put(key, true)
128+
}
129+
else {
130+
put(key, integrations[key]!!)
131+
}
132+
}
133+
}
134+
}
135+
136+
return this
137+
}
138+
139+
fun BaseEvent.enableCloudIntegrations(exceptKeys: List<String>? = null): BaseEvent {
140+
integrations = buildJsonObject {
141+
put(BaseEvent.ALL_INTEGRATIONS_KEY, true)
142+
143+
exceptKeys?.forEach { key ->
144+
put(key, false)
145+
}
146+
}
147+
100148
return this
101149
}

core/src/main/java/com/segment/analytics/kotlin/core/utilities/JSON.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,27 @@ fun JsonObject.transformKeys(transform: (String) -> String): JsonObject {
181181
fun JsonObject.transformValues(transform: (JsonElement) -> JsonElement): JsonObject {
182182
return JsonObject(this.mapValues { transform(it.value) })
183183
}
184+
185+
// Utility function to update a JsonObject. The updated copy is returned. Original copy is not touched
186+
fun updateJsonObject(jsonObject: JsonObject, closure: (MutableMap<String, JsonElement>) -> Unit): JsonObject {
187+
val content = jsonObject.toMutableMap()
188+
closure(content)
189+
return JsonObject(content)
190+
}
191+
192+
operator fun MutableMap<String, JsonElement>.set(key:String, value: String?) {
193+
if (value == null) {
194+
remove(key)
195+
}
196+
else {
197+
this[key] = JsonPrimitive(value)
198+
}
199+
}
200+
201+
operator fun MutableMap<String, JsonElement>.set(key:String, value: Number) {
202+
this[key] = JsonPrimitive(value)
203+
}
204+
205+
operator fun MutableMap<String, JsonElement>.set(key:String, value: Boolean) {
206+
this[key] = JsonPrimitive(value)
207+
}

core/src/test/kotlin/com/segment/analytics/kotlin/core/JSONTests.kt

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
package com.segment.analytics.kotlin.core
22

3-
import com.segment.analytics.kotlin.core.utilities.getBoolean
4-
import com.segment.analytics.kotlin.core.utilities.getDouble
5-
import com.segment.analytics.kotlin.core.utilities.getInt
6-
import com.segment.analytics.kotlin.core.utilities.getLong
7-
import com.segment.analytics.kotlin.core.utilities.getMapList
8-
import com.segment.analytics.kotlin.core.utilities.getString
9-
import com.segment.analytics.kotlin.core.utilities.getStringSet
10-
import com.segment.analytics.kotlin.core.utilities.mapTransform
11-
import com.segment.analytics.kotlin.core.utilities.putAll
12-
import com.segment.analytics.kotlin.core.utilities.putUndefinedIfNull
13-
import com.segment.analytics.kotlin.core.utilities.toContent
14-
import com.segment.analytics.kotlin.core.utilities.transformKeys
15-
import com.segment.analytics.kotlin.core.utilities.transformValues
3+
import com.segment.analytics.kotlin.core.utilities.*
164
import kotlinx.serialization.json.JsonPrimitive
175
import kotlinx.serialization.json.add
186
import kotlinx.serialization.json.boolean
@@ -733,4 +721,47 @@ class JSONTests {
733721
assertEquals("undefined", x.getString("foo"))
734722
}
735723
}
724+
725+
@Nested
726+
inner class UpdateJsonObjectTests {
727+
@Test
728+
fun `updateJsonObject updates values`() {
729+
var obj = emptyJsonObject
730+
731+
// test string
732+
obj = updateJsonObject(obj) {
733+
it["foo"] = "bar"
734+
}
735+
assertEquals(1, obj.size)
736+
assertEquals("bar", obj.getString("foo"))
737+
738+
// test number
739+
obj = updateJsonObject(obj) {
740+
it["foo"] = null
741+
it["number"] = 1
742+
}
743+
assertEquals(1, obj.size)
744+
assertEquals(1, obj.getInt("number"))
745+
746+
obj = updateJsonObject(obj) {
747+
it["number"] = 2.0
748+
}
749+
assertEquals(1, obj.size)
750+
assertEquals(2.0, obj.getDouble("number"))
751+
752+
obj = updateJsonObject(obj) {
753+
it["number"] = 4L
754+
}
755+
assertEquals(1, obj.size)
756+
assertEquals(4L, obj.getLong("number"))
757+
758+
// test boolean
759+
obj = updateJsonObject(obj) {
760+
it["number"] = null
761+
it["boolean"] = true
762+
}
763+
assertEquals(1, obj.size)
764+
assertEquals(true, obj.getBoolean("boolean"))
765+
}
766+
}
736767
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package com.segment.analytics.kotlin.core.utilities
2+
3+
import com.segment.analytics.kotlin.core.BaseEvent
4+
import com.segment.analytics.kotlin.core.TrackEvent
5+
import com.segment.analytics.kotlin.core.emptyJsonObject
6+
import kotlinx.serialization.json.buildJsonObject
7+
import kotlinx.serialization.json.put
8+
import org.junit.jupiter.api.Assertions.assertEquals
9+
import org.junit.jupiter.api.Test
10+
11+
internal class EventManipulationFunctionsTest {
12+
13+
@Test
14+
fun `enableIntegration enables integration when not exist`() {
15+
val trackEvent = TrackEvent(
16+
event = "clicked",
17+
properties = buildJsonObject { put("behaviour", "good") })
18+
.apply {
19+
messageId = "qwerty-1234"
20+
anonymousId = "anonId"
21+
integrations = emptyJsonObject
22+
context = emptyJsonObject
23+
}
24+
25+
trackEvent.enableIntegration("test")
26+
27+
assertEquals(true, trackEvent.integrations.getBoolean("test"))
28+
}
29+
30+
@Test
31+
fun `enableIntegration enables integration when disable`() {
32+
val trackEvent = TrackEvent(
33+
event = "clicked",
34+
properties = buildJsonObject { put("behaviour", "good") })
35+
.apply {
36+
messageId = "qwerty-1234"
37+
anonymousId = "anonId"
38+
integrations = buildJsonObject { put("test", false) }
39+
context = emptyJsonObject
40+
}
41+
42+
trackEvent.enableIntegration("test")
43+
44+
assertEquals(true, trackEvent.integrations.getBoolean("test"))
45+
}
46+
47+
@Test
48+
fun `enableIntegration enables integration when non-boolean`() {
49+
val jsonObject = buildJsonObject { put("key", "value") }
50+
val trackEvent = TrackEvent(
51+
event = "clicked",
52+
properties = buildJsonObject { put("behaviour", "good") })
53+
.apply {
54+
messageId = "qwerty-1234"
55+
anonymousId = "anonId"
56+
integrations = buildJsonObject { put("test", jsonObject) }
57+
context = emptyJsonObject
58+
}
59+
60+
trackEvent.enableIntegration("test")
61+
62+
assertEquals(jsonObject, trackEvent.integrations["test"])
63+
}
64+
65+
@Test
66+
fun `disableCloudIntegrations with except list`() {
67+
val jsonObject = buildJsonObject { put("key", "value") }
68+
val trackEvent = TrackEvent(
69+
event = "clicked",
70+
properties = buildJsonObject { put("behaviour", "good") })
71+
.apply {
72+
messageId = "qwerty-1234"
73+
anonymousId = "anonId"
74+
integrations = buildJsonObject {
75+
put("discard1", true)
76+
put("discard2", false)
77+
put("discard3", "string")
78+
put("discard4", jsonObject)
79+
put("keep1", true)
80+
put("keep2", false)
81+
put("keep3", "string")
82+
put("keep4", jsonObject)
83+
}
84+
context = emptyJsonObject
85+
}
86+
87+
trackEvent.disableCloudIntegrations(exceptKeys = listOf("keep1", "keep2", "keep3", "keep4"))
88+
89+
assertEquals(5, trackEvent.integrations.size)
90+
assertEquals(false, trackEvent.integrations.getBoolean(BaseEvent.ALL_INTEGRATIONS_KEY))
91+
assertEquals(true, trackEvent.integrations.getBoolean("keep1"))
92+
assertEquals(true, trackEvent.integrations.getBoolean("keep2"))
93+
assertEquals("string", trackEvent.integrations.getString("keep3"))
94+
assertEquals(jsonObject, trackEvent.integrations["keep4"])
95+
}
96+
97+
@Test
98+
fun `disableCloudIntegrations with null`() {
99+
val jsonObject = buildJsonObject { put("key", "value") }
100+
val trackEvent = TrackEvent(
101+
event = "clicked",
102+
properties = buildJsonObject { put("behaviour", "good") })
103+
.apply {
104+
messageId = "qwerty-1234"
105+
anonymousId = "anonId"
106+
integrations = buildJsonObject {
107+
put("discard1", true)
108+
put("discard2", false)
109+
put("discard3", "string")
110+
put("discard4", jsonObject)
111+
}
112+
context = emptyJsonObject
113+
}
114+
115+
trackEvent.disableCloudIntegrations()
116+
117+
assertEquals(1, trackEvent.integrations.size)
118+
assertEquals(false, trackEvent.integrations.getBoolean(BaseEvent.ALL_INTEGRATIONS_KEY))
119+
}
120+
121+
@Test
122+
fun `enableCloudIntegrations with except list`() {
123+
val jsonObject = buildJsonObject { put("key", "value") }
124+
val trackEvent = TrackEvent(
125+
event = "clicked",
126+
properties = buildJsonObject { put("behaviour", "good") })
127+
.apply {
128+
messageId = "qwerty-1234"
129+
anonymousId = "anonId"
130+
integrations = buildJsonObject {
131+
put("discard1", true)
132+
put("discard2", false)
133+
put("discard3", "string")
134+
put("discard4", jsonObject)
135+
put("keep1", true)
136+
put("keep2", false)
137+
put("keep3", "string")
138+
put("keep4", jsonObject)
139+
}
140+
context = emptyJsonObject
141+
}
142+
143+
trackEvent.enableCloudIntegrations(exceptKeys = listOf("keep1", "keep2", "keep3", "keep4"))
144+
145+
assertEquals(5, trackEvent.integrations.size)
146+
assertEquals(true, trackEvent.integrations.getBoolean(BaseEvent.ALL_INTEGRATIONS_KEY))
147+
assertEquals(false, trackEvent.integrations.getBoolean("keep1"))
148+
assertEquals(false, trackEvent.integrations.getBoolean("keep2"))
149+
assertEquals(false, trackEvent.integrations.getBoolean("keep3"))
150+
assertEquals(false, trackEvent.integrations.getBoolean("keep4"))
151+
}
152+
153+
@Test
154+
fun `enableCloudIntegrations with null`() {
155+
val jsonObject = buildJsonObject { put("key", "value") }
156+
val trackEvent = TrackEvent(
157+
event = "clicked",
158+
properties = buildJsonObject { put("behaviour", "good") })
159+
.apply {
160+
messageId = "qwerty-1234"
161+
anonymousId = "anonId"
162+
integrations = buildJsonObject {
163+
put("discard1", true)
164+
put("discard2", false)
165+
put("discard3", "string")
166+
put("discard4", jsonObject)
167+
}
168+
context = emptyJsonObject
169+
}
170+
171+
trackEvent.enableCloudIntegrations()
172+
173+
assertEquals(1, trackEvent.integrations.size)
174+
assertEquals(true, trackEvent.integrations.getBoolean(BaseEvent.ALL_INTEGRATIONS_KEY))
175+
}
176+
}

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,14 @@ class IntercomDestination(
209209

210210
private fun setCompany(company: JsonObject): Company {
211211
val builder = Company.Builder()
212-
company.getString("id")?.let {
213-
builder.withCompanyId(it)
214-
} ?: return builder.build()
212+
213+
val id = company.getString("id")
214+
if (id == null) {
215+
return builder.build()
216+
}
217+
else {
218+
builder.withCompanyId(id)
219+
}
215220

216221
company.getString(NAME)?.let { builder.withName(it) }
217222
company.getLong(CREATED_AT)?.let { builder.withCreatedAt(it) }

0 commit comments

Comments
 (0)