-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add identity module and file storage #8
Conversation
Somewhat off topic, I'll review the ID stuff separately. Just a question for you: Any reason you're creating dedicated threads instead of using I still appreciate the DI aspect of allowing for custom dispatchers, just feel that using the default would be more effective and optimized in most cases. |
@bgiori Thanks for the question. I guess the benefits for having custom dispatchers is we have more controls on our end. While Default and IO could be used and shared with other programs and that may cause some unexpected happen. I see segment is doing that as well so use this as a practice for us. We can do more testing and discuss further if we need to adjust this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll come back to review more again another day. Just want to get my initial thoughts out.
Also we could have a meeting to discuss details. Might be more efficient than over the github PR.
data class Identity( | ||
val userId: String? = null, | ||
val deviceId: String? = null, | ||
val userProperties: Map<String, Any?> = mapOf(), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO conflating userProperties with identity was a mistake on my part initially. I would not consider user properties as a part of a user's Identity. I.e. I think we should handle this separately. This also means we can remove the setUserProperties and updateUserProperties from the Editor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, if not handling user properties here then it may be easier for identity. Curious how we should handle this separately.
fun addIdentityListener(listener: IdentityListener) | ||
fun removeIdentityListener(listener: IdentityListener) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to go full on coroutines we could make this into a StateFlow<IdentityState>
where IdentityState
is an class of Identity
and IdentityUpdateType
If we do this, though, then the consumer must also be using kotlin. E.g. accessing coroutine objects from java is a PITA. If this is only an internal library called from other kotlin libraries then we should be able to go crazy with the coroutine api.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module would be an internal module though. Would there be any use case customer would like to access the data directly without analytics or experiment? If not I guess we can definitely consider this.
|
||
private var curFile: File? = null | ||
|
||
private val semaphore = Semaphore(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use a Mutex
which comes built in with a withLock
extension function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me take a look at Mutex
fun onUserIdChange(userId: String?) | ||
|
||
fun onDeviceIdChange(deviceId: String?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whats the use case for these two callbacks when onIdentityChanged could handle both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to separate out for analytics end to tell which has been updated. We can discuss more about this
enum class Constants(val rawVal: String) { | ||
LAST_EVENT_ID("last_event_id"), | ||
PREVIOUS_SESSION_ID("previous_session_id"), | ||
LAST_EVENT_TIME("last_event_time"), | ||
OPT_OUT("opt_out"), | ||
Events("events") | ||
} | ||
|
||
suspend fun writeEvent(event: BaseEvent) | ||
|
||
suspend fun write(key: Constants, value: String) | ||
|
||
suspend fun rollover() | ||
|
||
fun read(key: Constants): String? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be called EventStorage
to disambiguate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the interface? I thought about that once. Then kind of debate if I would need another storage to handle event id, session id, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I setup a time for us to sync tomorrow. Feel free to review more in the meantime.
enum class Constants(val rawVal: String) { | ||
LAST_EVENT_ID("last_event_id"), | ||
PREVIOUS_SESSION_ID("previous_session_id"), | ||
LAST_EVENT_TIME("last_event_time"), | ||
OPT_OUT("opt_out"), | ||
Events("events") | ||
} | ||
|
||
suspend fun writeEvent(event: BaseEvent) | ||
|
||
suspend fun write(key: Constants, value: String) | ||
|
||
suspend fun rollover() | ||
|
||
fun read(key: Constants): String? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the interface? I thought about that once. Then kind of debate if I would need another storage to handle event id, session id, etc.
|
||
private var curFile: File? = null | ||
|
||
private val semaphore = Semaphore(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me take a look at Mutex
fun onUserIdChange(userId: String?) | ||
|
||
fun onDeviceIdChange(deviceId: String?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to separate out for analytics end to tell which has been updated. We can discuss more about this
data class Identity( | ||
val userId: String? = null, | ||
val deviceId: String? = null, | ||
val userProperties: Map<String, Any?> = mapOf(), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, if not handling user properties here then it may be easier for identity. Curious how we should handle this separately.
fun addIdentityListener(listener: IdentityListener) | ||
fun removeIdentityListener(listener: IdentityListener) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module would be an internal module though. Would there be any use case customer would like to access the data directly without analytics or experiment? If not I guess we can definitely consider this.
@@ -100,3 +100,14 @@ abstract class DestinationPlugin: EventPlugin { | |||
} | |||
} | |||
|
|||
abstract class ObservePlugin: Plugin { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe IdObserverPlugin instead? Or do you see adding more non-id callbacks to this class?
|
||
fun setup(identityStore: IdentityStore) | ||
|
||
fun saveUserId(userId: String?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to name the methods in Storage
& IdentityStorage
interface to be more consistent? Storage.saveEvents()
vs Storage.writeEvents()
etc?
68e20c7
to
0b240ef
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WIP
0b240ef
to
2ec5db8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
# 1.0.0 (2022-06-28) ### Bug Fixes * fix android aar issue ([#32](#32)) ([69f5fe7](69f5fe7)) * fix group call ([#42](#42)) ([f84d143](f84d143)) ### Features * add amplitude destination plugin draft ([#3](#3)) ([7ee9408](7ee9408)) * add android context plugin ([#14](#14)) ([25b6da3](25b6da3)) * add basic test action and issue templates ([#9](#9)) ([e72c608](e72c608)) * add button to kotlin example to send custom event ([#41](#41)) ([5c1e573](5c1e573)) * add configuration java support ([#35](#35)) ([a4e0801](a4e0801)) * add error handling and retry ([#13](#13)) ([689b114](689b114)) * add eu and batch support ([#17](#17)) ([6402b2a](6402b2a)) * add event bridge module ([#12](#12)) ([49229ac](49229ac)) * add event id support ([#25](#25)) ([94aaa6b](94aaa6b)) * add github action about docs and release ([#19](#19)) ([3aa868e](3aa868e)) * add identify support ([#18](#18)) ([2f86adc](2f86adc)) * add identity module and file storage ([#8](#8)) ([8b7bada](8b7bada)) * add Java android example ([#24](#24)) ([2670a2b](2670a2b)) * add Kotlin android sample ([#23](#23)) ([3b948c8](3b948c8)) * add parner_id ([#16](#16)) ([5e46cb9](5e46cb9)) * add plan in configuration and export ([#34](#34)) ([d84a84e](d84a84e)) * add plan versionId support ([#22](#22)) ([ae84619](ae84619)) * add revenue helper class ([#6](#6)) ([341fcf1](341fcf1)) * add unit tests for common jvm module ([#29](#29)) ([dcc0e9d](dcc0e9d)) * add unit tests in core module ([#30](#30)) ([4283148](4283148)) * add unit tests in event bridge module ([#28](#28)) ([8d63c28](8d63c28)) * add unit tests in id module ([#27](#27)) ([dc78bc6](dc78bc6)) * android local sync update ([#20](#20)) ([e25ecd4](e25ecd4)) * enable to pass the event options for all track call ([#36](#36)) ([8dd1ab6](8dd1ab6)) * expose more api for support ([#33](#33)) ([312fd85](312fd85)) * make configuration open ([#38](#38)) ([d0dfb15](d0dfb15)) * remove json object from public interface ([#26](#26)) ([0840b5d](0840b5d)) * set the client userId and deviceId in identify call ([#37](#37)) ([2c8b3f1](2c8b3f1)) * set up android module bare bone ([#7](#7)) ([a8a8dc9](a8a8dc9)) * Set up jvm example ([#4](#4)) ([ae825d6](ae825d6)) * setup basic barebone ([#2](#2)) ([8e4b635](8e4b635)) * setup basic doc and workflow ([#1](#1)) ([31bd223](31bd223)) * setup common modules ([#11](#11)) ([4a4ff66](4a4ff66)) * update android storage ([#15](#15)) ([56c1d6a](56c1d6a)) * update library name ([#21](#21)) ([309976d](309976d))
🎉 This PR is included in version 1.0.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Summary
Checklist