Skip to content

Commit b65247c

Browse files
authored
Wenxi/fix identity doc (#57)
* fix identify doc * add reset * add unit tests * attempt to fix flaky tests in JavaAnalyticsTest.kt * attempt to fix flaky tests in JavaAnalyticsTest.kt * add findAll and optimize code * add unit tests for find all * fix flaky tests * fix flaky tests * fix flaky tests * replace instance check with more idiomatic way
1 parent c358d77 commit b65247c

File tree

17 files changed

+224
-91
lines changed

17 files changed

+224
-91
lines changed

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ class Analytics internal constructor(
4343
val storage: Storage
4444
companion object {
4545
var debugLogsEnabled: Boolean = false
46-
set(value: Boolean) {
46+
set(value) {
4747
SegmentLog.loggingEnabled = value
48+
field = value
4849
}
4950
}
5051

@@ -151,9 +152,12 @@ class Analytics internal constructor(
151152
* etc.
152153
*
153154
* <p>Traits and userId will be automatically cached and available on future sessions for the
154-
* same user. To update a trait on the server, call identify with the same user id (or null).
155+
* same user. To update a trait on the server, call identify with the same user id.
155156
* You can also use {@link #identify(Traits)} for this purpose.
156157
*
158+
* In the case when user logs out, make sure to call {@link #reset()} to clear user's identity
159+
* info.
160+
*
157161
* @param userId Unique identifier which you recognize a user by in your own database
158162
* @param traits [Traits] about the user.
159163
* @see <a href="https://segment.com/docs/spec/identify/">Identify Documentation</a>
@@ -173,9 +177,12 @@ class Analytics internal constructor(
173177
* etc.
174178
*
175179
* <p>Traits and userId will be automatically cached and available on future sessions for the
176-
* same user. To update a trait on the server, call identify with the same user id (or null).
180+
* same user. To update a trait on the server, call identify with the same user id.
177181
* You can also use {@link #identify(Traits)} for this purpose.
178182
*
183+
* In the case when user logs out, make sure to call {@link #reset()} to clear user's identity
184+
* info.
185+
*
179186
* @param userId Unique identifier which you recognize a user by in your own database
180187
* @param traits [Traits] about the user. Needs to be [serializable](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md)
181188
* @param serializationStrategy strategy to serialize [traits]
@@ -195,9 +202,12 @@ class Analytics internal constructor(
195202
* etc.
196203
*
197204
* <p>Traits and userId will be automatically cached and available on future sessions for the
198-
* same user. To update a trait on the server, call identify with the same user id (or null).
205+
* same user. To update a trait on the server, call identify with the same user id.
199206
* You can also use {@link #identify(Traits)} for this purpose.
200207
*
208+
* In the case when user logs out, make sure to call {@link #reset()} to clear user's identity
209+
* info.
210+
*
201211
* @param userId Unique identifier which you recognize a user by in your own database
202212
* @param traits [Traits] about the user. Needs to be [serializable](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md)
203213
* @see <a href="https://segment.com/docs/spec/identify/">Identify Documentation</a>
@@ -381,11 +391,21 @@ class Analytics internal constructor(
381391
}
382392

383393
/**
384-
* Retrieve a registered plugin by reference
385-
* @param plugin [Plugin]
394+
* Retrieve the first match of registered plugin. It finds
395+
* 1. the first instance of the given class/interface
396+
* 2. or the first instance of subclass of the given class/interface
397+
* @param plugin [KClass]
386398
*/
387399
fun <T: Plugin> find(plugin: KClass<T>): T? = this.timeline.find(plugin)
388400

401+
/**
402+
* Retrieve the first match of registered plugin. It finds
403+
* 1. all instances of the given class/interface
404+
* 2. and all instances of subclass of the given class/interface
405+
* @param plugin [KClass]
406+
*/
407+
fun <T: Plugin> findAll(plugin: KClass<T>): List<T> = this.timeline.findAll(plugin)
408+
389409
/**
390410
* Remove a plugin from the analytics timeline using its name
391411
* @param plugin [Plugin] to be remove
@@ -414,6 +434,19 @@ class Analytics internal constructor(
414434
}
415435
}
416436

437+
/**
438+
* Reset the user identity info and all the event plugins. Should be invoked when
439+
* user logs out
440+
*/
441+
fun reset() {
442+
analyticsScope.launch(analyticsDispatcher) {
443+
store.dispatch(UserInfo.ResetAction(), UserInfo::class)
444+
timeline.applyClosure {
445+
(it as? EventPlugin)?.reset()
446+
}
447+
}
448+
}
449+
417450
/**
418451
* Retrieve the userId registered by a previous `identify` call in a blocking way.
419452
* Note: this method invokes `runBlocking` internal, it's not recommended to be used

core/src/main/java/com/segment/analytics/kotlin/core/compat/JavaAnalytics.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ class JavaAnalytics private constructor() {
7979
* same user. To update a trait on the server, call identify with the same user id (or null).
8080
* You can also use {@link #identify(Traits)} for this purpose.
8181
*
82+
* In the case when user logs out, make sure to call {@link #reset()} to clear user's identity
83+
* info.
84+
*
8285
* @param userId Unique identifier which you recognize a user by in your own database
8386
* @param traits [Traits] about the user in JsonObject form. can be built by
8487
* {@link Builder com.segment.analytics.kotlin.core.compat.Builder}
@@ -96,6 +99,9 @@ class JavaAnalytics private constructor() {
9699
* same user. To update a trait on the server, call identify with the same user id (or null).
97100
* You can also use {@link #identify(Traits)} for this purpose.
98101
*
102+
* In the case when user logs out, make sure to call {@link #reset()} to clear user's identity
103+
* info.
104+
*
99105
* @param userId Unique identifier which you recognize a user by in your own database
100106
* @param serializable an object that implements {@link JsonSerializable com.segment.analytics.kotlin.core.compat.JsonSerializable}
101107
* @see <a href="https://segment.com/docs/spec/identify/">Identify Documentation</a>
@@ -212,6 +218,12 @@ class JavaAnalytics private constructor() {
212218

213219
fun flush() = analytics.flush()
214220

221+
/**
222+
* Reset the user identity info and all the event plugins. Should be invoked when
223+
* user logs out
224+
*/
225+
fun reset() = analytics.reset()
226+
215227
/**
216228
* Retrieve the userId registered by a previous `identify` call
217229
*/

core/src/main/java/com/segment/analytics/kotlin/core/platform/Mediator.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,14 @@ internal class Mediator(internal val plugins: MutableList<Plugin>) {
6565

6666
fun <T: Plugin> find(pluginClass: KClass<T>): T? = synchronized(plugins) {
6767
plugins.forEach {
68-
if (it::class == pluginClass) {
68+
if (pluginClass.isInstance(it)) {
6969
return it as T
7070
}
7171
}
7272
return null
7373
}
74+
75+
fun <T: Plugin> findAll(pluginClass: KClass<T>): List<T> = synchronized(plugins) {
76+
return plugins.filter { pluginClass.isInstance(it) } as List<T>
77+
}
7478
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,13 @@ internal class Timeline {
9090
}
9191
return null
9292
}
93+
94+
fun <T: Plugin> findAll(pluginClass: KClass<T>): List<T> {
95+
val result = mutableListOf<T>()
96+
plugins.forEach { (_, list) ->
97+
val found = list.findAll(pluginClass)
98+
result.addAll(found)
99+
}
100+
return result
101+
}
93102
}

core/src/main/java/com/segment/analytics/kotlin/core/platform/plugins/logger/LogTarget.kt

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,10 @@ fun Analytics.log(message: String, kind: LogFilterKind? = null, function: String
126126
return
127127
}
128128

129-
applyClosureToPlugins { plugin: Plugin ->
130-
if (plugin is SegmentLog) {
131-
var filterKind = plugin.filterKind
132-
if (kind != null) {
133-
filterKind = kind
134-
}
135-
136-
val log = LogFactory.buildLog(LoggingType.Filter.LOG, "", message, filterKind, function, line)
137-
plugin.log(log, LoggingType.Filter.LOG)
138-
}
129+
val plugins = findAll(SegmentLog::class)
130+
plugins.forEach {
131+
val log = LogFactory.buildLog(LoggingType.Filter.LOG, "", message, kind ?: it.filterKind, function, line)
132+
it.log(log, LoggingType.Filter.LOG)
139133
}
140134
}
141135

@@ -156,11 +150,10 @@ fun Analytics.metric(type: String, name: String, value: Double, tags: List<Strin
156150
return
157151
}
158152

159-
applyClosureToPlugins { plugin: Plugin ->
160-
if (plugin is SegmentLog) {
161-
val log = LogFactory.buildLog(LoggingType.Filter.METRIC, type, name, value = value, tags = tags)
162-
plugin.log(log, LoggingType.Filter.METRIC)
163-
}
153+
val log = LogFactory.buildLog(LoggingType.Filter.METRIC, type, name, value = value, tags = tags)
154+
val plugins = findAll(SegmentLog::class)
155+
plugins.forEach {
156+
it.log(log, LoggingType.Filter.METRIC)
164157
}
165158
}
166159

@@ -190,17 +183,16 @@ fun Analytics.history(event: BaseEvent, sender: Any, function: String = "", line
190183
updatedLine = methodInfo.second
191184
}
192185

193-
applyClosureToPlugins { plugin: Plugin ->
194-
if (plugin is SegmentLog) {
195-
val log = LogFactory.buildLog(LoggingType.Filter.HISTORY,
196-
title = event.toString(),
197-
message = "",
198-
function = updatedFunction,
199-
line = updatedLine,
200-
event = event,
201-
sender = sender)
202-
plugin.log(log, LoggingType.Filter.HISTORY)
203-
}
186+
val log = LogFactory.buildLog(LoggingType.Filter.HISTORY,
187+
title = event.toString(),
188+
message = "",
189+
function = updatedFunction,
190+
line = updatedLine,
191+
event = event,
192+
sender = sender)
193+
val plugins = findAll(SegmentLog::class)
194+
plugins.forEach {
195+
it.log(log, LoggingType.Filter.HISTORY)
204196
}
205197
}
206198

@@ -214,10 +206,9 @@ fun Analytics.history(event: BaseEvent, sender: Any, function: String = "", line
214206
* public API on Analytics.
215207
*/
216208
fun Analytics.add(target: LogTarget, type: LoggingType) {
217-
applyClosureToPlugins { plugin: Plugin ->
218-
if (plugin is SegmentLog) {
219-
plugin.add(target, type)
220-
}
209+
val plugins = findAll(SegmentLog::class)
210+
plugins.forEach {
211+
it.add(target, type)
221212
}
222213
}
223214

@@ -228,20 +219,18 @@ fun Analytics.add(target: LogTarget, type: LoggingType) {
228219
* @property targetType A `LogTarget` class type that should be removed from the system.
229220
*/
230221
fun <T: LogTarget> Analytics.remove(targetType: KClass<T>) {
231-
applyClosureToPlugins { plugin: Plugin ->
232-
if (plugin is SegmentLog) {
233-
plugin.remove(targetType)
234-
}
222+
val plugins = findAll(SegmentLog::class)
223+
plugins.forEach {
224+
it.remove(targetType)
235225
}
236226
}
237227

238228
/**
239229
* Expunge all cached logs that have been captured.
240230
*/
241231
fun Analytics.logFlush() {
242-
this.timeline.applyClosure { plugin: Plugin ->
243-
if (plugin is SegmentLog) {
244-
plugin.flush()
245-
}
232+
val plugins = findAll(SegmentLog::class)
233+
plugins.forEach {
234+
it.flush()
246235
}
247236
}

core/src/main/java/com/segment/analytics/kotlin/core/platform/plugins/logger/SegmentLog.kt

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -158,35 +158,36 @@ fun Analytics.Companion.segmentLog(message: String, kind: LogFilterKind? = LogFi
158158

159159
val methodInfo = Analytics.callingMethodDetails(function, line)
160160

161-
SegmentLog.sharedAnalytics?.applyClosureToPlugins { plugin: Plugin ->
162-
if (plugin is SegmentLog) {
163-
var filterKind = plugin.filterKind
164-
if (kind != null) {
165-
filterKind = kind
166-
}
167-
168-
val log = LogFactory.buildLog(LoggingType.Filter.LOG, "", message, filterKind, methodInfo.first, methodInfo.second)
169-
plugin.log(log, LoggingType.Filter.LOG)
170-
}
161+
val plugins = SegmentLog.sharedAnalytics?.findAll(SegmentLog::class)
162+
163+
plugins?.forEach {
164+
val log = LogFactory.buildLog(
165+
LoggingType.Filter.LOG,
166+
"",
167+
message,
168+
kind ?: it.filterKind,
169+
methodInfo.first,
170+
methodInfo.second
171+
)
172+
it.log(log, LoggingType.Filter.LOG)
171173
}
172174
}
173175

174176
fun Analytics.Companion.segmentMetric(type: String, name: String, value: Double, tags: List<String>?) {
175-
SegmentLog.sharedAnalytics?.applyClosureToPlugins { plugin: Plugin ->
176-
if (plugin is SegmentLog) {
177-
val log = LogFactory.buildLog(LoggingType.Filter.METRIC,
178-
type,
179-
name,
180-
LogFilterKind.DEBUG,
181-
null,
182-
null,
183-
null,
184-
null,
185-
value,
186-
tags)
187-
plugin.log(log, LoggingType.Filter.METRIC)
188-
// TODO: Capture function and line
189-
}
177+
val log = LogFactory.buildLog(LoggingType.Filter.METRIC,
178+
type,
179+
name,
180+
LogFilterKind.DEBUG,
181+
null,
182+
null,
183+
null,
184+
null,
185+
value,
186+
tags)
187+
188+
val plugins = SegmentLog.sharedAnalytics?.findAll(SegmentLog::class)
189+
plugins?.forEach {
190+
it.log(log, LoggingType.Filter.METRIC)
190191
}
191192
}
192193

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ class PropertiesFile(private val directory: File, writeKey: String) : KVS {
2222
if (propertiesFile.exists()) {
2323
underlyingProperties.load(FileInputStream(propertiesFile))
2424
}
25+
else {
26+
propertiesFile.parentFile.mkdirs()
27+
propertiesFile.createNewFile()
28+
}
2529
}
2630

2731
fun save() {

0 commit comments

Comments
 (0)