Skip to content

Commit 6816df4

Browse files
authored
Merge pull request #1687 from OneSignal/user-model/track-session-start
[User Model] Track Session Start
2 parents 95fd7c1 + 86b872e commit 6816df4

File tree

10 files changed

+94
-30
lines changed

10 files changed

+94
-30
lines changed

OneSignalSDK/onesignal/src/main/java/com/onesignal/core/internal/operations/impl/OperationModelStore.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import com.onesignal.user.internal.operations.SetAliasOperation
1313
import com.onesignal.user.internal.operations.SetPropertyOperation
1414
import com.onesignal.user.internal.operations.SetTagOperation
1515
import com.onesignal.user.internal.operations.TrackPurchaseOperation
16-
import com.onesignal.user.internal.operations.TrackSessionOperation
16+
import com.onesignal.user.internal.operations.TrackSessionEndOperation
17+
import com.onesignal.user.internal.operations.TrackSessionStartOperation
1718
import com.onesignal.user.internal.operations.UpdateSubscriptionOperation
1819
import com.onesignal.user.internal.operations.impl.executors.IdentityOperationExecutor
1920
import com.onesignal.user.internal.operations.impl.executors.LoginUserOperationExecutor
@@ -49,7 +50,8 @@ internal class OperationModelStore(prefs: IPreferencesService) : ModelStore<Oper
4950
UpdateUserOperationExecutor.SET_TAG -> SetTagOperation()
5051
UpdateUserOperationExecutor.DELETE_TAG -> DeleteTagOperation()
5152
UpdateUserOperationExecutor.SET_PROPERTY -> SetPropertyOperation()
52-
UpdateUserOperationExecutor.TRACK_SESSION -> TrackSessionOperation()
53+
UpdateUserOperationExecutor.TRACK_SESSION_START -> TrackSessionStartOperation()
54+
UpdateUserOperationExecutor.TRACK_SESSION_END -> TrackSessionEndOperation()
5355
UpdateUserOperationExecutor.TRACK_PURCHASE -> TrackPurchaseOperation()
5456
else -> throw Exception("Unrecognized operation: $operationName")
5557
}

OneSignalSDK/onesignal/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,21 @@ import com.onesignal.session.internal.outcomes.IOutcomeEventsController
88
import com.onesignal.session.internal.session.ISessionLifecycleHandler
99
import com.onesignal.session.internal.session.ISessionService
1010
import com.onesignal.user.internal.identity.IdentityModelStore
11-
import com.onesignal.user.internal.operations.TrackSessionOperation
11+
import com.onesignal.user.internal.operations.TrackSessionEndOperation
12+
import com.onesignal.user.internal.operations.TrackSessionStartOperation
1213

1314
/**
1415
* The [SessionListener] is responsible for subscribing itself as an [ISessionLifecycleHandler]
15-
* and reacting to the session ending. Specifically we need to do 2 things whenever a session
16-
* has ended.
16+
* and reacting to the session ending.
1717
*
18-
* 1. Enqueue a [TrackSessionOperation] to the [IOperationRepo] so session data will be counted
18+
* We need to do the following on session start:
19+
*
20+
* 1. Enqueue a [TrackSessionStartOperation] to the [IOperationRepo] so session data will be counted
21+
* against the user.
22+
*
23+
* We need to do the following on session end:
24+
*
25+
* 1. Enqueue a [TrackSessionEndOperation] to the [IOperationRepo] so session data will be counted
1926
* against the user.
2027
* 2. Send up an internal `__os_session_end` outcome, which will pick up any influences that led
2128
* to the session existing.
@@ -33,13 +40,14 @@ internal class SessionListener(
3340
}
3441

3542
override fun onSessionStarted() {
43+
_operationRepo.enqueue(TrackSessionStartOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId))
3644
}
3745

3846
override fun onSessionActive() {
3947
}
4048

4149
override fun onSessionEnded(duration: Long) {
42-
_operationRepo.enqueue(TrackSessionOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, duration))
50+
_operationRepo.enqueue(TrackSessionEndOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, duration))
4351

4452
suspendifyOnThread {
4553
_outcomeEventsController.sendOutcomeEvent("os__session_duration")

OneSignalSDK/onesignal/src/main/java/com/onesignal/session/internal/session/impl/SessionService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ internal class SessionService(
5454
}
5555

5656
override suspend fun backgroundRun() {
57-
Logging.log(LogLevel.DEBUG, "SessionService.run()")
57+
Logging.log(LogLevel.DEBUG, "SessionService.backgroundRun()")
5858

5959
if (!_session!!.isValid) {
6060
return

OneSignalSDK/onesignal/src/main/java/com/onesignal/user/internal/backend/PropertiesDeltasObject.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import java.math.BigDecimal
44

55
class PropertiesDeltasObject(
66
val sessionTime: Long? = null,
7-
val sessionCounts: Int? = null,
7+
val sessionCount: Int? = null,
88
val amountSpent: BigDecimal? = null,
99
val purchases: List<PurchaseObject>? = null
1010
)

OneSignalSDK/onesignal/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ internal class UserBackendService(
118118
deltasObject.put("session_time", propertiesDeltas.sessionTime)
119119
}
120120

121-
if (propertiesDeltas.sessionCounts != null) {
122-
deltasObject.put("session_counts", propertiesDeltas.sessionCounts)
121+
if (propertiesDeltas.sessionCount != null) {
122+
deltasObject.put("session_counts", propertiesDeltas.sessionCount)
123123
}
124124

125125
if (propertiesDeltas.amountSpent != null) {

OneSignalSDK/onesignal/src/main/java/com/onesignal/user/internal/operations/TrackSessionOperation.kt renamed to OneSignalSDK/onesignal/src/main/java/com/onesignal/user/internal/operations/TrackSessionEndOperation.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import com.onesignal.user.internal.operations.impl.executors.UpdateUserOperation
88
/**
99
* An [Operation] to track the ending of a session, related to a specific user.
1010
*/
11-
class TrackSessionOperation() : Operation(UpdateUserOperationExecutor.TRACK_SESSION) {
11+
class TrackSessionEndOperation() : Operation(UpdateUserOperationExecutor.TRACK_SESSION_END) {
1212
/**
1313
* The OneSignal appId the session was captured under.
1414
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.onesignal.user.internal.operations
2+
3+
import com.onesignal.common.IDManager
4+
import com.onesignal.core.internal.operations.GroupComparisonType
5+
import com.onesignal.core.internal.operations.Operation
6+
import com.onesignal.user.internal.operations.impl.executors.UpdateUserOperationExecutor
7+
8+
/**
9+
* An [Operation] to track the starting of a session, related to a specific user.
10+
*/
11+
class TrackSessionStartOperation() : Operation(UpdateUserOperationExecutor.TRACK_SESSION_START) {
12+
/**
13+
* The OneSignal appId the session was captured under.
14+
*/
15+
var appId: String
16+
get() = getProperty(::appId.name)
17+
private set(value) { setProperty(::appId.name, value) }
18+
19+
/**
20+
* The OneSignal ID driving the session. This ID *may* be locally generated
21+
* and can be checked via [IDManager.isLocalId] to ensure correct processing.
22+
*/
23+
var onesignalId: String
24+
get() = getProperty(::onesignalId.name)
25+
private set(value) { setProperty(::onesignalId.name, value) }
26+
27+
override val createComparisonKey: String get() = ""
28+
override val modifyComparisonKey: String get() = "$appId.User.$onesignalId"
29+
override val groupComparisonType: GroupComparisonType = GroupComparisonType.ALTER
30+
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
31+
32+
constructor(appId: String, onesignalId: String) : this() {
33+
this.appId = appId
34+
this.onesignalId = onesignalId
35+
}
36+
37+
override fun translateIds(map: Map<String, String>) {
38+
if (map.containsKey(onesignalId)) {
39+
onesignalId = map[onesignalId]!!
40+
}
41+
}
42+
}

OneSignalSDK/onesignal/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import com.onesignal.user.internal.operations.DeleteTagOperation
1919
import com.onesignal.user.internal.operations.SetPropertyOperation
2020
import com.onesignal.user.internal.operations.SetTagOperation
2121
import com.onesignal.user.internal.operations.TrackPurchaseOperation
22-
import com.onesignal.user.internal.operations.TrackSessionOperation
22+
import com.onesignal.user.internal.operations.TrackSessionEndOperation
23+
import com.onesignal.user.internal.operations.TrackSessionStartOperation
2324
import com.onesignal.user.internal.properties.PropertiesModelStore
2425

2526
internal class UpdateUserOperationExecutor(
@@ -29,7 +30,7 @@ internal class UpdateUserOperationExecutor(
2930
) : IOperationExecutor {
3031

3132
override val operations: List<String>
32-
get() = listOf(SET_TAG, DELETE_TAG, SET_PROPERTY, TRACK_SESSION, TRACK_PURCHASE)
33+
get() = listOf(SET_TAG, DELETE_TAG, SET_PROPERTY, TRACK_SESSION_START, TRACK_SESSION_END, TRACK_PURCHASE)
3334

3435
override suspend fun execute(ops: List<Operation>): ExecutionResponse {
3536
Logging.log(LogLevel.DEBUG, "UserOperationExecutor(operation: $operations)")
@@ -39,6 +40,7 @@ internal class UpdateUserOperationExecutor(
3940

4041
var propertiesObject = PropertiesObject()
4142
var deltasObject = PropertiesDeltasObject()
43+
var refreshDeviceMetadata = false
4244

4345
for (operation in ops) {
4446
when (operation) {
@@ -66,20 +68,32 @@ internal class UpdateUserOperationExecutor(
6668

6769
propertiesObject = PropertyOperationHelper.createPropertiesFromOperation(operation, propertiesObject)
6870
}
69-
is TrackSessionOperation -> {
71+
is TrackSessionStartOperation -> {
7072
if (appId == null) {
7173
appId = operation.appId
7274
onesignalId = operation.onesignalId
7375
}
7476

75-
// The session count we pass up is essentially the number of `TrackSessionOperation`
77+
// The session count we pass up is essentially the number of `TrackSessionStartOperation`
7678
// operations we come across in this group, while the session time we pass up is
7779
// the total session time across all `TrackSessionOperation` operations
7880
// that exist in this group.
81+
val sessionCount = if (deltasObject.sessionCount != null) deltasObject.sessionCount!! + 1 else 1
82+
83+
deltasObject = PropertiesDeltasObject(deltasObject.sessionTime, sessionCount, deltasObject.amountSpent, deltasObject.purchases)
84+
refreshDeviceMetadata = true
85+
}
86+
is TrackSessionEndOperation -> {
87+
if (appId == null) {
88+
appId = operation.appId
89+
onesignalId = operation.onesignalId
90+
}
91+
92+
// The session time we pass up is the total session time across all `TrackSessionEndOperation`
93+
// operations that exist in this group.
7994
val sessionTime = if (deltasObject.sessionTime != null) deltasObject.sessionTime!! + operation.sessionTime else operation.sessionTime
80-
val sessionCount = if (deltasObject.sessionCounts != null) deltasObject.sessionCounts!! + 1 else 1
8195

82-
deltasObject = PropertiesDeltasObject(sessionTime, sessionCount, deltasObject.amountSpent, deltasObject.purchases)
96+
deltasObject = PropertiesDeltasObject(sessionTime, deltasObject.sessionCount, deltasObject.amountSpent, deltasObject.purchases)
8397
}
8498
is TrackPurchaseOperation -> {
8599
if (appId == null) {
@@ -97,14 +111,14 @@ internal class UpdateUserOperationExecutor(
97111
purchasesArray.add(PurchaseObject(purchase.sku, purchase.iso, purchase.amount))
98112
}
99113

100-
deltasObject = PropertiesDeltasObject(deltasObject.sessionTime, deltasObject.sessionCounts, amountSpent, purchasesArray)
114+
deltasObject = PropertiesDeltasObject(deltasObject.sessionTime, deltasObject.sessionCount, amountSpent, purchasesArray)
101115
}
102116
}
103117
}
104118

105119
if (appId != null && onesignalId != null) {
106120
try {
107-
_userBackend.updateUser(appId, IdentityConstants.ONESIGNAL_ID, onesignalId, propertiesObject, true, deltasObject)
121+
_userBackend.updateUser(appId, IdentityConstants.ONESIGNAL_ID, onesignalId, propertiesObject, refreshDeviceMetadata, deltasObject)
108122

109123
if (_identityModelStore.model.onesignalId == onesignalId) {
110124
// go through and make sure any properties are in the correct model state
@@ -132,7 +146,8 @@ internal class UpdateUserOperationExecutor(
132146
const val SET_TAG = "set-tag"
133147
const val DELETE_TAG = "delete-tag"
134148
const val SET_PROPERTY = "set-property"
135-
const val TRACK_SESSION = "track-session"
149+
const val TRACK_SESSION_START = "track-session-start"
150+
const val TRACK_SESSION_END = "track-session-end"
136151
const val TRACK_PURCHASE = "track-purchase"
137152
}
138153
}

OneSignalSDK/onesignal/src/test/java/com/onesignal/user/internal/backend/UserBackendServiceTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ class UserBackendServiceTests : FunSpec({
342342
coEvery { spyHttpClient.post(any(), any()) } returns HttpResponse(202, "{properties: { }}")
343343
val userBackendService = UserBackendService(spyHttpClient, spySubscriptionBackend)
344344
val properties = PropertiesObject()
345-
val propertiesDelta = PropertiesDeltasObject(sessionTime = 1111, sessionCounts = 1)
345+
val propertiesDelta = PropertiesDeltasObject(sessionTime = 1111, sessionCount = 1)
346346

347347
/* When */
348348
userBackendService.updateUser("appId", aliasLabel, aliasValue, properties, refreshDeviceMetadata = true, propertiesDelta)

OneSignalSDK/onesignal/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class UpdateUserOperationExecutorTests : FunSpec({
131131
mockPropertiesModelStore
132132
)
133133
val operations = listOf<Operation>(
134-
TrackSessionOperation(appId, remoteOneSignalId, 1111)
134+
TrackSessionEndOperation(appId, remoteOneSignalId, 1111)
135135
)
136136

137137
/* When */
@@ -150,7 +150,6 @@ class UpdateUserOperationExecutorTests : FunSpec({
150150
any(),
151151
withArg {
152152
it.sessionTime shouldBe 1111
153-
it.sessionCounts shouldBe 1
154153
}
155154
)
156155
}
@@ -171,7 +170,7 @@ class UpdateUserOperationExecutorTests : FunSpec({
171170
mockPropertiesModelStore
172171
)
173172
val operations = listOf<Operation>(
174-
TrackSessionOperation(appId, remoteOneSignalId, 1111),
173+
TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
175174
TrackPurchaseOperation(
176175
appId,
177176
remoteOneSignalId,
@@ -182,7 +181,7 @@ class UpdateUserOperationExecutorTests : FunSpec({
182181
PurchaseInfo("sku2", "iso2", BigDecimal(1222))
183182
)
184183
),
185-
TrackSessionOperation(appId, remoteOneSignalId, 3333)
184+
TrackSessionEndOperation(appId, remoteOneSignalId, 3333)
186185
)
187186

188187
/* When */
@@ -201,7 +200,6 @@ class UpdateUserOperationExecutorTests : FunSpec({
201200
any(),
202201
withArg {
203202
it.sessionTime shouldBe (1111 + 3333)
204-
it.sessionCounts shouldBe 2
205203
it.amountSpent shouldBe BigDecimal(2222)
206204
it.purchases shouldNotBe null
207205
it.purchases!!.count() shouldBe 2
@@ -231,9 +229,9 @@ class UpdateUserOperationExecutorTests : FunSpec({
231229
mockPropertiesModelStore
232230
)
233231
val operations = listOf<Operation>(
234-
TrackSessionOperation(appId, remoteOneSignalId, 1111),
232+
TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
235233
SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"),
236-
TrackSessionOperation(appId, remoteOneSignalId, 3333)
234+
TrackSessionEndOperation(appId, remoteOneSignalId, 3333)
237235
)
238236

239237
/* When */
@@ -252,7 +250,6 @@ class UpdateUserOperationExecutorTests : FunSpec({
252250
any(),
253251
withArg {
254252
it.sessionTime shouldBe (1111 + 3333)
255-
it.sessionCounts shouldBe 2
256253
}
257254
)
258255
}

0 commit comments

Comments
 (0)