-
Notifications
You must be signed in to change notification settings - Fork 376
Feat: Detect for timezone changes and update the user #2378
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
base: main
Are you sure you want to change the base?
Conversation
Tack onto existing SessionListener's TrackSessionStartOperation call so this update would be combined. Otherwise, because the TrackSessionStartOperation has flush = true, the timing may mean 2 user update requests are made when it could be combined. Cons: the Session Listener is encroaching beyond what its scope should be.
dbb8bde to
f5dc3c9
Compare
After the implementation to start updating timezone changes on new sessions, hydrating timezone from the user refresh call will conflict if the timing between the update and refresh requests is not perfect. For example, imagine this scenario if we still hydrate this property: 1. On a new session, timezone detects as changed from US to UK. A property update is enqueued. 2. This request is sent and the refresh user call returns (which still contains timezone as US) 3. The SDK hydrates timezone as US 4. On the next session, timezone is detected as changed from US to UK.
f5dc3c9 to
6018f4d
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.
Must write unit tests
.../onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
Outdated
Show resolved
Hide resolved
| if (response.properties.timezoneId != null) { | ||
| propertiesModel.timezone = response.properties.timezoneId | ||
| } | ||
| // No longer hydrate timezone from remote, set locally |
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 with this.
| } | ||
|
|
||
| fun update() { | ||
| timezone = TimeUtils.getTimeZoneId() |
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.
does this TimeUtils.getTimeZoneId() match with what the backend really expects?
Can we write a test to make sure that?
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.
Yeah, it does. Currently, the only place the SDK ever sends timezone is in a create user call here:
// the non-null assert is actually extraneous
properties["timezone_id"] = TimeUtils.getTimeZoneId()!!
does this TimeUtils.getTimeZoneId() match with what the backend really expects? Can we write a test to make sure that?
I don't know how we could write a test for what the server expects. We could write a test that ensures timezone is always sent as a certain format, but if the backend ever changed its expectation, we wouldn't know.
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 are right, we cant test server expectations directly. But we should write tests to catch internal breaking changes.
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 the model classes should be very simple and all setting should done outside of it. So we shouldn't add a helper like this that is set a specific implementation of getting timezone.
| propertiesModel.timezone = response.properties.timezoneId | ||
| } | ||
| // No longer hydrate timezone from remote, set locally | ||
| propertiesModel.update() |
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.
Should this be done on a background thread?
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 comment as before, propertiesModel.update() just sets propertiesModel.timezone = TimeUtils.getTimeZoneId(). If we look at the lines above this, we are setting various properties on the propertiesModel like:
propertiesModel.country = response.properties.country
propertiesModel.tags[tagKVP.key] = tagKVP.value!!
propertiesModel.language = response.properties.language
Setting timezone shouldn't run separately while this object is being set up. Also, during this time while the model is being set up, no updates will fire an operation to enqueue.
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.
sounds good around the background thread. but we definitely need to write a test if the properties.update() is called and if the timezone is actually set.
this helps in possible regression defects and helps us gain confidence when we are making changes.
Yeah, I mentioned in PR that I will add if implementation is fine. I put the logic in the SessionListener, but for true separation of concerns, it should probably be in UserManager or something similar. However, putting in SessionListener can batch this operation with the track session start operation so we don't send 2 requests. But I'll make sure to write tests for the cases you pointed out. |
| } | ||
|
|
||
| fun update() { | ||
| timezone = TimeUtils.getTimeZoneId() |
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 the model classes should be very simple and all setting should done outside of it. So we shouldn't add a helper like this that is set a specific implementation of getting timezone.
.../onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
Outdated
Show resolved
Hide resolved
|
that makes sense to me as well @jkasten2 . Though I wonder if timezone would really changes between every app session. And i am guessing you would prefer to be more careful so better to do it onFocus? Would it be more optimized if we update the timezone in the properties only if different from the current one. That way we dont update everytime on onFocus. |
I suggested using The best option would be if our SDK listens to timezone changes from the system (Android APIs) and can send that to our server. That way we always have the latest correct value without the user needing to open the app. Example code of how that can be done: However it's pretty complex to setup the Android APIs. Using something like onFocus gets us 90% of the benefits, is something that we can be happy with long term, and matches closer to iOS's behavior.
I believe our models already takes care of not doing anything if the value didn't change. |
Let the UserManager refresh the changes to properties model, instead of SessionListener. The SubscriptionManager already refreshes the push subscription.
2b8d221 to
e433a4f
Compare
|
@jkasten2 @abdulraqeeb33 - I pushed a commit to have UserManager instead detect changes in AR started adding tests in #2382 🙏 |
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.
thank you creating a new pr for tests
|
|
||
| override fun onFocus(firedOnSubscribe: Boolean) { | ||
| // Detect any user properties updates that changed | ||
| _propertiesModel.timezone = TimeUtils.getTimeZoneId() |
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 want to pass TimeUtils into this class? so that you can mock it?
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.
also please make sure to write tests
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.
As the tests are in the other PR
Let's not merge this until then, we can pick those commits over when done |
|
Hi @abdulraqeeb33 I used the changes you made in #2382 and modified to add the tests here. Let me know if a4a6d5e looks good to you. |
* Add UserManagerTests that onFocus will update timezone * Update an existing test in RefreshUserOperationExecutorTests that tests a successful getUser request and hydration already. Let's add on the timezone component to this hydration, which is that timezone is no longer hydrated from the server but set locally. * Add TimeUtilsTest class testing getTimeZoneId
It's already non-null
bf179a3 to
14b5d76
Compare
Description
One Line Summary
Start detecting timezone changes and sending it up when it changes.
Details
Motivation
This was a request of clients so that when users enter new timezones, they can be properly segmented without needing a Create User call to happen.
Scope
nullwhich will trigger a change detection).Testing
Unit testing
Added
Manual testing
Change timezone on emulator and see the next session, that timezone is sent with the track session start call.
Affected code checklist
Checklist
Overview
Testing
Final pass
This change is