-
Notifications
You must be signed in to change notification settings - Fork 23
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
refactor: device attributes using data pipeline #434
Conversation
Sample app builds 📱Below you will find the list of the latest versions of the sample apps. It's recommended to always download the latest builds of the sample apps to accurately test the pull request.
|
public static func initialize( | ||
writeKey: String, | ||
configure configureHandler: ((inout DataPipelineConfigOptions) -> Void)? = nil | ||
) -> CustomerIOInstance { |
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.
Removed this as we are now using DataPipeline.initialize(moduleConfig: DataPipelineConfigOptions)
private func initialize() { | ||
if let token = globalDataStore.pushDeviceToken { | ||
// if the device token exists, pass it to the plugin to ensure device attributes are updated with each request | ||
setDeviceToken(token) |
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.
Wasn't able to test complete flow because Journey implementation is not yet complete. But testing with assumptions and looking at code, it looks like segment is storing token only in memory, so we apparently need to pass it on every app launch.
Adds device default and custom attributes and registers device token. | ||
*/ | ||
// TODO: Segment doesn't provide this method by default needs to get added | ||
private func addDeviceAttributes(deviceToken: String, customAttributes: [String: Any] = [:]) { |
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.
With DataPipelineImplementation
handling all this stuff, we may completely remove CustomerIOImplementation
, but doing it in phases so it is easier for us to make sure we aren't ignoring anything unknowingly.
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.
Update: We will be keeping CustomerIOImplementation
as it will help us keep the same experience for existing users without impacting customer coming from Segment.
} | ||
|
||
private func initialize() { | ||
if let token = globalDataStore.pushDeviceToken { |
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.
we might not need do it anymore with journeys inclusion
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.
Discussed this on call, will decide after discussing more with team. Looks good for now.
@@ -6,24 +6,31 @@ class DeviceAttributes: Plugin { | |||
public let type = PluginType.before | |||
public weak var analytics: Analytics? | |||
|
|||
public var token: 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.
We are now not using segment plugin for token and relying on our plugin instead
@@ -6,6 +6,7 @@ SHELL = /bin/sh | |||
# imports - Import statements to be at the top of the generated files in case the file needs classes from other modules. Split by `-` (example: `imports=Cio-Foo-Bar`) | |||
generate: | |||
./binny sourcery --sources Sources/Common --templates Sources/Templates --output Sources/Common/autogenerated | |||
./binny sourcery --sources Sources/DataPipeline --templates Sources/Templates --output Sources/DataPipeline/autogenerated --args imports=CioInternalCommon |
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.
Added this for DeviceAttributesProvider
@@ -57,4 +57,7 @@ public struct DataPipelineConfigOptions { | |||
public var flushQueue: DispatchQueue = .init(label: "com.segment.operatingModeQueue", qos: .utility) | |||
public var operatingMode: OperatingMode = .asynchronous | |||
public var trackApplicationLifecycleEvents: Bool = true | |||
|
|||
/// Configuration options for users to easily add available plugins | |||
public var autoTrackDeviceAttributes: Bool = true |
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.
QUESTION
This option might be new for analytics
users, but same for Customer.io users. And analytics
would still be adding some attributes, should we stop them too or remove this config completely?
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.
Could you ask this in the slack channel? I want to make sure it doesn't go unanswered.
onComplete([:]) | ||
return | ||
} | ||
|
||
var deviceAttributes = [ | ||
"cio_sdk_version": getSdkVersionAttribute(), | ||
"cio_sdk_version": deviceInfo.sdkVersion, |
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.
Because wrappers will have their own analytics implementation, I think we can simply refer sdkVersion
now
guard let identifier = profileStore.identifier else { | ||
logger.info("no profile identified, so not registering device token to a profile") | ||
return | ||
} | ||
if identifier.isBlankOrEmpty() { | ||
logger.error("profile cannot be identified: Identifier is empty, so not registering device token to a profile") | ||
return | ||
} | ||
|
||
// OS name might not be available if running on non-apple product. We currently only support iOS for the SDK | ||
// and iOS should always be non-nil. Though, we are consolidating all Apple platforms under iOS but this check | ||
// is | ||
// required to prevent SDK execution for unsupported OS. | ||
if deviceInfo.osName == nil { | ||
logger.info("SDK being executed from unsupported OS. Ignoring request to register push token.") | ||
return | ||
} |
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.
We are no longer blocking token calls on these checks because
- Device token can now be attached before profile is identified
analytics
doesn't prevent unknown OS names from registering token, so to keepanalytics
user unaffected, we are no longer blocking this. However, if needed for we can keep this check inCustomerIOImplementation.swift
with slight changes
guard let existingDeviceToken = globalDataStore.pushDeviceToken else { | ||
logger.info("no device token exists so ignoring request to delete") | ||
return // no device token to delete, ignore request | ||
} | ||
// Do not delete push token from device storage. The token is valid | ||
// once given to SDK. We need it for future profile identifications. | ||
|
||
guard let identifiedProfileId = profileStore.identifier else { | ||
logger.info("no profile identified so not removing device token from profile") | ||
return // no profile to delete token from, ignore request | ||
} |
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.
Same as above, no checks because analytics
doesn't do that either. And customers may still want to reset data for anonymous users.
deviceAttributesProvider.getDefaultDeviceAttributes { defaultDeviceAttributes in | ||
let deviceAttributes: [String: Any] = defaultDeviceAttributes | ||
.mergeWith([ | ||
"last_used": self.dateUtil.now |
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.
Removed platform
as it causes issues on server side, we no longer need it
Device and metric events json have been verified. Merging this as the changes are required on other branches for testing and code changes. |
if let existingDeviceToken = globalDataStore.pushDeviceToken { | ||
// if the device token exists, pass it to the plugin and ensure device attributes are updated | ||
addDeviceAttributes(token: existingDeviceToken) | ||
} |
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 believe that this logic can be removed once we have the eventbus implemented and the CDP module begins listening to register device token events from the eventbus.
Correct, @Shahroz16?
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 this is true, could we make a TODO
or something to document this change?
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 think we will need to see the use-case as its using a plugin, i probably transforming data on basis of event.
guard var workingEvent = event, | ||
var context = workingEvent.context?.dictionaryValue | ||
else { return event } |
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.
What is this doing? From reading it, I don't understand what this code is for?
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.
transforming the data, if you look into the payload
expected by CDP and generated by SDK that might help in understand it more.
Raw Event
already has a json, which has context
and other json object
in it. This plugin updates the values of context
json object.
} | ||
} catch { | ||
analytics?.reportInternalError(error) |
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.
Since this file is code that we wrote, I was expecting to use the CIO logger, logger.error()
. Could that work better?
helps: https://github.com/customerio/issues/issues/11609
Changes
initialize
fromDataPipeline
DataPipelineImplementation
to set device attributes usinganalytics
CustomerIOImplementation