-
Notifications
You must be signed in to change notification settings - Fork 9
chore: make location module self-contained with generic core interfaces #664
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
Merged
Merged
Changes from 3 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 0 additions & 5 deletions
5
core/src/main/kotlin/io/customer/sdk/communication/LocationCache.kt
This file was deleted.
Oops, something went wrong.
17 changes: 17 additions & 0 deletions
17
core/src/main/kotlin/io/customer/sdk/core/pipeline/DataPipeline.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package io.customer.sdk.core.pipeline | ||
|
|
||
| import io.customer.base.internal.InternalCustomerIOApi | ||
|
|
||
| /** | ||
| * Abstraction for sending track events to the data pipeline. | ||
| * | ||
| * Modules retrieve an implementation via `SDKComponent.getOrNull<DataPipeline>()` | ||
| * to send events directly without going through EventBus. | ||
| * | ||
| * This is an internal SDK contract — not intended for use by host app developers. | ||
| */ | ||
| @InternalCustomerIOApi | ||
| interface DataPipeline { | ||
| val isUserIdentified: Boolean | ||
| fun track(name: String, properties: Map<String, Any?>) | ||
| } |
20 changes: 20 additions & 0 deletions
20
core/src/main/kotlin/io/customer/sdk/core/pipeline/IdentifyContextProvider.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package io.customer.sdk.core.pipeline | ||
|
|
||
| import io.customer.base.internal.InternalCustomerIOApi | ||
|
|
||
| /** | ||
| * Interface for modules that contribute context entries to identify events. | ||
| * | ||
| * Implementations return a map of primitive-valued entries (String, Number, Boolean) | ||
| * that will be added to the identify event's context via `putInContext()`. | ||
| * Return an empty map when there is nothing to contribute. | ||
| * | ||
| * These are NOT profile traits/attributes — they are context-level enrichment | ||
| * data (e.g., location coordinates) attached to the identify event payload. | ||
| * | ||
| * This is an internal SDK contract — not intended for use by host app developers. | ||
| */ | ||
| @InternalCustomerIOApi | ||
| interface IdentifyContextProvider { | ||
| fun getIdentifyContext(): Map<String, Any> | ||
| } |
41 changes: 41 additions & 0 deletions
41
core/src/main/kotlin/io/customer/sdk/core/pipeline/IdentifyContextRegistry.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package io.customer.sdk.core.pipeline | ||
|
|
||
| import io.customer.base.internal.InternalCustomerIOApi | ||
| import io.customer.sdk.core.di.SDKComponent | ||
|
|
||
| /** | ||
| * Thread-safe registry of [IdentifyContextProvider] instances. | ||
| * | ||
| * Modules register providers during initialization. The datapipelines module | ||
| * queries all providers when enriching identify event context. | ||
| * | ||
| * Cleared automatically when [SDKComponent.reset] clears singletons. | ||
| * | ||
| * This is an internal SDK contract — not intended for use by host app developers. | ||
| */ | ||
| @InternalCustomerIOApi | ||
| class IdentifyContextRegistry { | ||
| private val providers = mutableListOf<IdentifyContextProvider>() | ||
|
|
||
| @Synchronized | ||
| fun register(provider: IdentifyContextProvider) { | ||
| if (provider !in providers) { | ||
| providers.add(provider) | ||
| } | ||
| } | ||
|
|
||
| @Synchronized | ||
| fun getAll(): List<IdentifyContextProvider> = providers.toList() | ||
|
|
||
| @Synchronized | ||
| fun clear() { | ||
| providers.clear() | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Singleton accessor for [IdentifyContextRegistry] via [SDKComponent]. | ||
| */ | ||
| @InternalCustomerIOApi | ||
| val SDKComponent.identifyContextRegistry: IdentifyContextRegistry | ||
| get() = singleton { IdentifyContextRegistry() } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
datapipelines/src/main/kotlin/io/customer/datapipelines/plugins/IdentifyContextPlugin.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package io.customer.datapipelines.plugins | ||
|
|
||
| import com.segment.analytics.kotlin.core.Analytics | ||
| import com.segment.analytics.kotlin.core.BaseEvent | ||
| import com.segment.analytics.kotlin.core.IdentifyEvent | ||
| import com.segment.analytics.kotlin.core.platform.EventPlugin | ||
| import com.segment.analytics.kotlin.core.platform.Plugin | ||
| import com.segment.analytics.kotlin.core.utilities.putInContext | ||
| import io.customer.sdk.core.pipeline.IdentifyContextRegistry | ||
| import io.customer.sdk.core.util.Logger | ||
| import kotlinx.serialization.json.JsonPrimitive | ||
|
|
||
| /** | ||
| * Generic Segment enrichment plugin that queries all registered | ||
| * [IdentifyContextProvider][io.customer.sdk.core.pipeline.IdentifyContextProvider] | ||
| * instances and adds their entries to the identify event context. | ||
| * | ||
| * This plugin has zero knowledge of specific modules — providers | ||
| * manage their own state and return primitive-valued maps. | ||
| */ | ||
| internal class IdentifyContextPlugin( | ||
| private val registry: IdentifyContextRegistry, | ||
| private val logger: Logger | ||
| ) : EventPlugin { | ||
| override val type: Plugin.Type = Plugin.Type.Enrichment | ||
| override lateinit var analytics: Analytics | ||
|
|
||
| override fun identify(payload: IdentifyEvent): BaseEvent { | ||
| for (provider in registry.getAll()) { | ||
| try { | ||
| val context = provider.getIdentifyContext() | ||
| if (context.isEmpty()) continue | ||
| for ((key, value) in context) { | ||
| val jsonValue = when (value) { | ||
| is String -> JsonPrimitive(value) | ||
| is Number -> JsonPrimitive(value) | ||
| is Boolean -> JsonPrimitive(value) | ||
| else -> { | ||
| logger.debug("Skipping non-primitive context entry: $key") | ||
| continue | ||
| } | ||
| } | ||
| payload.putInContext(key, jsonValue) | ||
| } | ||
| } catch (e: Exception) { | ||
| logger.error("IdentifyContextProvider failed: ${e.message}") | ||
| } | ||
| } | ||
| return payload | ||
| } | ||
| } | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
36 changes: 0 additions & 36 deletions
36
datapipelines/src/main/kotlin/io/customer/datapipelines/plugins/LocationPlugin.kt
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Are we sure that
@Synchronizedis the right tool here? Do plugins use threads or coroutines?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.
Pluginsrun synchronously on the caller's thread,EventPlugin.identify()is blocking and executes on whatever thread calledanalytics.identify(). No coroutines involved at the plugin level.@Synchronizedis the right tool here becauseregister(),getAll(), andclear()are fast, non-suspending operations (list add/copy/clear). A coroutine Mutex would require these to be suspend functions, which doesn't fit the EventPlugin interface.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.
The main idea is that the registry is called from other modules to register themselves and this could expand to more modules in the future. So we can't be sure that this invariant remains true