Skip to content

Commit 977130c

Browse files
authored
Merge pull request #1793 from OneSignal/user_model/fix_outcome_requests
[5.0.0] Fix outcome requests
2 parents fda8ee2 + b1b4d99 commit 977130c

File tree

16 files changed

+156
-50
lines changed

16 files changed

+156
-50
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/database/impl/OSDatabase.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import com.onesignal.core.internal.database.IDatabase
1616
import com.onesignal.debug.internal.logging.Logging
1717
import com.onesignal.session.internal.outcomes.impl.OutcomeTableProvider
1818
import com.onesignal.session.internal.outcomes.impl.OutcomesDbContract.SQL_CREATE_OUTCOME_ENTRIES_V1
19-
import com.onesignal.session.internal.outcomes.impl.OutcomesDbContract.SQL_CREATE_OUTCOME_ENTRIES_V3
19+
import com.onesignal.session.internal.outcomes.impl.OutcomesDbContract.SQL_CREATE_OUTCOME_ENTRIES_V4
2020
import com.onesignal.session.internal.outcomes.impl.OutcomesDbContract.SQL_CREATE_UNIQUE_OUTCOME_ENTRIES_V1
2121
import com.onesignal.session.internal.outcomes.impl.OutcomesDbContract.SQL_CREATE_UNIQUE_OUTCOME_ENTRIES_V2
2222

@@ -246,7 +246,7 @@ internal open class OSDatabase(
246246

247247
override fun onCreate(db: SQLiteDatabase) {
248248
db.execSQL(SQL_CREATE_ENTRIES)
249-
db.execSQL(SQL_CREATE_OUTCOME_ENTRIES_V3)
249+
db.execSQL(SQL_CREATE_OUTCOME_ENTRIES_V4)
250250
db.execSQL(SQL_CREATE_UNIQUE_OUTCOME_ENTRIES_V2)
251251
db.execSQL(SQL_CREATE_IN_APP_MESSAGE_ENTRIES)
252252
for (ind in SQL_INDEX_ENTRIES) {
@@ -277,6 +277,7 @@ internal open class OSDatabase(
277277
if (oldVersion == 5 && newVersion >= 6) upgradeFromV5ToV6(db)
278278
if (oldVersion < 7 && newVersion >= 7) upgradeToV7(db)
279279
if (oldVersion < 8 && newVersion >= 8) upgradeToV8(db)
280+
if (oldVersion < 9 && newVersion >= 9) upgradeToV9(db)
280281
}
281282

282283
// Add collapse_id field and index
@@ -342,6 +343,10 @@ internal open class OSDatabase(
342343
_outcomeTableProvider.upgradeCacheOutcomeTableRevision1To2(db)
343344
}
344345

346+
private fun upgradeToV9(db: SQLiteDatabase) {
347+
_outcomeTableProvider.upgradeOutcomeTableRevision3To4(db)
348+
}
349+
345350
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
346351
Logging.warn("SDK version rolled back! Clearing $DATABASE_NAME as it could be in an unexpected state.")
347352

@@ -357,7 +362,7 @@ internal open class OSDatabase(
357362
}
358363

359364
companion object {
360-
private const val dbVersion = 8
365+
private const val dbVersion = 9
361366
private val LOCK = Any()
362367
private const val DATABASE_NAME = "OneSignal.db"
363368
private const val INTEGER_PRIMARY_KEY_TYPE = " INTEGER PRIMARY KEY"

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/IOutcomeEvent.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ interface IOutcomeEvent {
88
val notificationIds: JSONArray?
99
val name: String
1010
val timestamp: Long
11+
val sessionTime: Long // in seconds
1112
val weight: Float
1213
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/IOutcomeEventsController.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ package com.onesignal.session.internal.outcomes
44
* The gateway to outcomes logic.
55
*/
66
interface IOutcomeEventsController {
7+
/**
8+
* Send a session ending outcome event to the backend.
9+
*/
10+
suspend fun sendSessionEndOutcomeEvent(duration: Long): IOutcomeEvent?
11+
712
/**
813
* Send a unique outcome event to the backend.
914
*/

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/IOutcomeEventsBackendService.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ internal interface IOutcomeEventsBackendService {
1515
* @param appId The ID of the application this outcome event occurred under.
1616
* @param userId The OneSignal user ID that is active during the outcome event.
1717
* @param subscriptionId The subscription ID that is active during the outcome event.
18+
* @param deviceType The type of device that the outcome event occurred on.
1819
* @param direct Whether this outcome event is direct. `true` if it is, `false` if it isn't, `null` if should not be specified.
1920
* @param event The outcome event to send up.
2021
*/
21-
suspend fun sendOutcomeEvent(appId: String, userId: String, subscriptionId: String, direct: Boolean?, event: OutcomeEvent)
22+
suspend fun sendOutcomeEvent(appId: String, userId: String, subscriptionId: String, deviceType: String, direct: Boolean?, event: OutcomeEvent)
2223
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomeConstants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ internal object OutcomeConstants {
66
const val OUTCOME_SOURCES = "sources"
77
const val WEIGHT = "weight"
88
const val TIMESTAMP = "timestamp"
9+
const val SESSION_TIME = "session_time"
910

1011
// OSOutcomeSource Constants
1112
const val DIRECT = "direct"

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomeEvent.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal class OutcomeEvent(
1111
override val notificationIds: JSONArray?,
1212
override val name: String,
1313
override val timestamp: Long,
14+
override val sessionTime: Long,
1415
override val weight: Float,
1516
) : IOutcomeEvent {
1617
@Throws(JSONException::class)
@@ -20,6 +21,7 @@ internal class OutcomeEvent(
2021
json.put(NOTIFICATION_IDS, notificationIds)
2122
json.put(OUTCOME_ID, name)
2223
json.put(TIMESTAMP, timestamp)
24+
json.put(SESSION_TIME, sessionTime)
2325
json.put(WEIGHT, weight)
2426
return json
2527
}
@@ -28,11 +30,11 @@ internal class OutcomeEvent(
2830
if (this === o) return true
2931
if (o == null || this.javaClass != o.javaClass) return false
3032
val event = o as OutcomeEvent
31-
return session == event.session && notificationIds == event.notificationIds && name == event.name && timestamp == event.timestamp && weight == event.weight
33+
return session == event.session && notificationIds == event.notificationIds && name == event.name && timestamp == event.timestamp && sessionTime == event.sessionTime && weight == event.weight
3234
}
3335

3436
override fun hashCode(): Int {
35-
val a = arrayOf(session, notificationIds, name, timestamp, weight)
37+
val a = arrayOf(session, notificationIds, name, timestamp, sessionTime, weight)
3638
var result = 1
3739
for (element in a) result = 31 * result + (element?.hashCode() ?: 0)
3840
return result
@@ -44,6 +46,7 @@ internal class OutcomeEvent(
4446
", notificationIds=" + notificationIds +
4547
", name='" + name + '\'' +
4648
", timestamp=" + timestamp +
49+
", sessionTime=" + sessionTime +
4750
", weight=" + weight +
4851
'}'
4952
}
@@ -53,6 +56,7 @@ internal class OutcomeEvent(
5356
private const val NOTIFICATION_IDS = "notification_ids"
5457
private const val OUTCOME_ID = "id"
5558
private const val TIMESTAMP = "timestamp"
59+
private const val SESSION_TIME = "session_time"
5660
private const val WEIGHT = "weight"
5761

5862
/**
@@ -76,6 +80,7 @@ internal class OutcomeEvent(
7680
notificationId,
7781
outcomeEventParams.outcomeId,
7882
outcomeEventParams.timestamp,
83+
outcomeEventParams.sessionTime,
7984
outcomeEventParams.weight,
8085
)
8186
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomeEventParams.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ internal class OutcomeEventParams constructor(
77
val outcomeId: String,
88
val outcomeSource: OutcomeSource?, // This field is optional
99
var weight: Float, // This field is optional.
10-
var timestamp: Long = 0,
10+
var sessionTime: Long, // This field is optional
11+
var timestamp: Long, // This should start out as zero
1112
) {
1213
@Throws(JSONException::class)
1314
fun toJSONObject(): JSONObject {
@@ -18,6 +19,7 @@ internal class OutcomeEventParams constructor(
1819
}
1920
if (weight > 0) json.put(OutcomeConstants.WEIGHT, weight)
2021
if (timestamp > 0) json.put(OutcomeConstants.TIMESTAMP, timestamp)
22+
if (sessionTime > 0) json.put(OutcomeConstants.SESSION_TIME, sessionTime)
2123
return json
2224
}
2325

@@ -29,6 +31,7 @@ internal class OutcomeEventParams constructor(
2931
", outcomeSource=" + outcomeSource +
3032
", weight=" + weight +
3133
", timestamp=" + timestamp +
34+
", sessionTime=" + sessionTime +
3235
'}'
3336
}
3437
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomeEventsBackendService.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import org.json.JSONObject
77
internal class OutcomeEventsBackendService(private val _http: IHttpClient) :
88
IOutcomeEventsBackendService {
99

10-
override suspend fun sendOutcomeEvent(appId: String, userId: String, subscriptionId: String, direct: Boolean?, event: OutcomeEvent) {
10+
override suspend fun sendOutcomeEvent(appId: String, userId: String, subscriptionId: String, deviceType: String, direct: Boolean?, event: OutcomeEvent) {
1111
val jsonObject = JSONObject()
1212
.put("app_id", appId)
1313
.put("onesignal_id", userId)
1414
.put(
1515
"subscription",
1616
JSONObject()
17-
.put("id", subscriptionId),
17+
.put("id", subscriptionId)
18+
.put("type", deviceType)
1819
)
1920

2021
if (direct != null) {
@@ -34,6 +35,10 @@ internal class OutcomeEventsBackendService(private val _http: IHttpClient) :
3435
jsonObject.put("timestamp", event.timestamp)
3536
}
3637

38+
if (event.sessionTime > 0) {
39+
jsonObject.put("session_time", event.sessionTime)
40+
}
41+
3742
val response = _http.post("outcomes/measure", jsonObject)
3843

3944
if (!response.isSuccess) {

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomeEventsController.kt

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.os.Process
44
import com.onesignal.common.exceptions.BackendException
55
import com.onesignal.common.threading.suspendifyOnThread
66
import com.onesignal.core.internal.config.ConfigModelStore
7+
import com.onesignal.core.internal.device.IDeviceService
78
import com.onesignal.core.internal.startup.IStartableService
89
import com.onesignal.core.internal.time.ITime
910
import com.onesignal.debug.internal.logging.Logging
@@ -14,6 +15,7 @@ import com.onesignal.session.internal.influence.InfluenceType
1415
import com.onesignal.session.internal.outcomes.IOutcomeEventsController
1516
import com.onesignal.session.internal.session.ISessionLifecycleHandler
1617
import com.onesignal.session.internal.session.ISessionService
18+
import com.onesignal.user.internal.backend.SubscriptionObjectType
1719
import com.onesignal.user.internal.identity.IdentityModelStore
1820
import com.onesignal.user.internal.subscriptions.ISubscriptionManager
1921

@@ -26,6 +28,7 @@ internal class OutcomeEventsController(
2628
private val _configModelStore: ConfigModelStore,
2729
private val _identityModelStore: IdentityModelStore,
2830
private val _subscriptionManager: ISubscriptionManager,
31+
private val _deviceService: IDeviceService,
2932
private val _time: ITime,
3033
) : IOutcomeEventsController, IStartableService, ISessionLifecycleHandler {
3134
// Keeps track of unique outcome events sent for UNATTRIBUTED sessions on a per session level
@@ -78,19 +81,31 @@ Outcome event was cached and will be reattempted on app cold start""",
7881
}
7982
}
8083

84+
override suspend fun sendSessionEndOutcomeEvent(duration: Long): OutcomeEvent? {
85+
val influences: List<Influence> = _influenceManager.influences
86+
87+
// only send the outcome if there are any influences associated with the session
88+
for (influence in influences) {
89+
if (influence.ids != null) {
90+
return sendAndCreateOutcomeEvent("os__session_duration", 0f, duration, influences)
91+
}
92+
}
93+
return null
94+
}
95+
8196
override suspend fun sendUniqueOutcomeEvent(name: String): OutcomeEvent? {
8297
val sessionResult: List<Influence> = _influenceManager.influences
8398
return sendUniqueOutcomeEvent(name, sessionResult)
8499
}
85100

86101
override suspend fun sendOutcomeEvent(name: String): OutcomeEvent? {
87102
val influences: List<Influence> = _influenceManager.influences
88-
return sendAndCreateOutcomeEvent(name, 0f, influences)
103+
return sendAndCreateOutcomeEvent(name, 0f, 0, influences)
89104
}
90105

91106
override suspend fun sendOutcomeEventWithValue(name: String, weight: Float): OutcomeEvent? {
92107
val influences: List<Influence> = _influenceManager.influences
93-
return sendAndCreateOutcomeEvent(name, weight, influences)
108+
return sendAndCreateOutcomeEvent(name, weight, 0, influences)
94109
}
95110

96111
/**
@@ -131,7 +146,7 @@ Outcome event was cached and will be reattempted on app cold start""",
131146
// Return null to determine not a failure, but not a success in terms of the request made
132147
return null
133148
}
134-
return sendAndCreateOutcomeEvent(name, 0f, uniqueInfluences)
149+
return sendAndCreateOutcomeEvent(name, 0f, 0, uniqueInfluences)
135150
} else {
136151
// Make sure unique outcome has not been sent for current unattributed session
137152
if (unattributedUniqueOutcomeEventsSentOnSession.contains(name)) {
@@ -147,13 +162,14 @@ Outcome event was cached and will be reattempted on app cold start""",
147162
return null
148163
}
149164
unattributedUniqueOutcomeEventsSentOnSession.add(name)
150-
return sendAndCreateOutcomeEvent(name, 0f, influences)
165+
return sendAndCreateOutcomeEvent(name, 0f, 0, influences)
151166
}
152167
}
153168

154169
private suspend fun sendAndCreateOutcomeEvent(
155170
name: String,
156171
weight: Float,
172+
sessionTime: Long, // Note: this is optional
157173
influences: List<Influence>,
158174
): OutcomeEvent? {
159175
val timestampSeconds: Long = _time.currentTimeMillis / 1000
@@ -183,7 +199,7 @@ Outcome event was cached and will be reattempted on app cold start""",
183199
}
184200

185201
val source = OutcomeSource(directSourceBody, indirectSourceBody)
186-
val eventParams = OutcomeEventParams(name, source, weight, 0)
202+
val eventParams = OutcomeEventParams(name, source, weight, sessionTime, 0)
187203

188204
try {
189205
requestMeasureOutcomeEvent(eventParams)
@@ -267,10 +283,11 @@ Outcome event was cached and will be reattempted on app cold start""",
267283
private suspend fun requestMeasureOutcomeEvent(eventParams: OutcomeEventParams) {
268284
val appId: String = _configModelStore.model.appId
269285
val subscriptionId = _subscriptionManager.subscriptions.push.id
286+
val deviceType = SubscriptionObjectType.fromDeviceType(_deviceService.deviceType).value
270287

271288
// if we don't have a subscription ID yet, throw an exception. The outcome will be saved and processed
272289
// later, when we do have a subscription ID.
273-
if (subscriptionId.isEmpty()) {
290+
if (subscriptionId.isEmpty() || deviceType.isEmpty()) {
274291
throw BackendException(0)
275292
}
276293

@@ -282,6 +299,6 @@ Outcome event was cached and will be reattempted on app cold start""",
282299
else -> null
283300
}
284301

285-
_outcomeEventsBackend.sendOutcomeEvent(appId, _identityModelStore.model.onesignalId, subscriptionId, direct, event)
302+
_outcomeEventsBackend.sendOutcomeEvent(appId, _identityModelStore.model.onesignalId, subscriptionId, deviceType, direct, event)
286303
}
287304
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomeEventsRepository.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ internal class OutcomeEventsRepository(
8888
put(OutcomeEventsTable.COLUMN_NAME_NAME, eventParams.outcomeId)
8989
put(OutcomeEventsTable.COLUMN_NAME_WEIGHT, eventParams.weight)
9090
put(OutcomeEventsTable.COLUMN_NAME_TIMESTAMP, eventParams.timestamp)
91+
put(OutcomeEventsTable.COLUMN_NAME_SESSION_TIME, eventParams.sessionTime)
9192
}.also { values ->
9293
_databaseProvider.os.insert(OutcomeEventsTable.TABLE_NAME, null, values)
9394
}
@@ -128,6 +129,8 @@ internal class OutcomeEventsRepository(
128129
cursor.getFloat(OutcomeEventsTable.COLUMN_NAME_WEIGHT)
129130
val timestamp =
130131
cursor.getLong(OutcomeEventsTable.COLUMN_NAME_TIMESTAMP)
132+
val sessionTime =
133+
cursor.getLong(OutcomeEventsTable.COLUMN_NAME_SESSION_TIME)
131134

132135
try {
133136
val directSourceBody = OutcomeSourceBody()
@@ -147,7 +150,7 @@ internal class OutcomeEventsRepository(
147150
it,
148151
)
149152
} ?: OutcomeSource(null, null)
150-
OutcomeEventParams(name, source, weight, timestamp).also {
153+
OutcomeEventParams(name, source, weight, sessionTime, timestamp).also {
151154
events.add(it)
152155
}
153156
} catch (e: JSONException) {

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomeTableProvider.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,24 @@ internal class OutcomeTableProvider {
7878
}
7979
}
8080

81+
/**
82+
* On the outcome table this adds the new session_time column.
83+
*
84+
* @param db
85+
*/
86+
fun upgradeOutcomeTableRevision3To4(db: SQLiteDatabase) {
87+
try {
88+
db.execSQL("BEGIN TRANSACTION;")
89+
db.execSQL("ALTER TABLE " + OutcomeEventsTable.TABLE_NAME + " ADD COLUMN " + OutcomeEventsTable.COLUMN_NAME_SESSION_TIME + " INTEGER DEFAULT 1;")
90+
// We intentionally choose to default session_time to 1 to address a bug on cached outcomes from v5.0.0-beta's
91+
// os__session_duration requests expect a session_time and these will keep failing and caching, so let's just send them with a time of 1 for migrations
92+
} catch (e: SQLiteException) {
93+
e.printStackTrace()
94+
} finally {
95+
db.execSQL("COMMIT;")
96+
}
97+
}
98+
8199
/**
82100
* On the cache unique outcome table rename table, rename column notification id to influence id
83101
* Add column channel type

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/outcomes/impl/OutcomesDbContract.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ internal object OutcomeEventsTable {
2020
const val COLUMN_NAME_WEIGHT = "weight" // Added on DB v5 SDK v3.12.1, migration added on DB v6 SDK v3.12.2
2121
const val COLUMN_NAME_TIMESTAMP = "timestamp" // Added on DB v4 SDK v3.12.0
2222
const val COLUMN_NAME_PARAMS = "params" // Added on DB v4 SDK v3.12.0 replaced with weight on DB v5 SDK v3.12.1, migration added on DB v6 SDK v3.12.2
23+
24+
// Session time
25+
const val COLUMN_NAME_SESSION_TIME = "session_time" // Added on DB v9 SDK v5.0.0 (note that 5.0.0-beta's were still on v8)
2326
}
2427

2528
internal object CachedUniqueOutcomeTable {
@@ -72,6 +75,22 @@ internal object OutcomesDbContract {
7275
OutcomeEventsTable.COLUMN_NAME_TIMESTAMP + TIMESTAMP_TYPE + "," + // "params TEXT" Added in v4, removed in v5.
7376
OutcomeEventsTable.COLUMN_NAME_WEIGHT + FLOAT_TYPE + // New in v5, missing migration added in v6
7477
");"
78+
79+
/**
80+
* Adds a new column called session_time
81+
*/
82+
const val SQL_CREATE_OUTCOME_ENTRIES_V4 = "CREATE TABLE " + OutcomeEventsTable.TABLE_NAME + " (" +
83+
OutcomeEventsTable.ID + INTEGER_PRIMARY_KEY_TYPE + "," +
84+
OutcomeEventsTable.COLUMN_NAME_NOTIFICATION_INFLUENCE_TYPE + TEXT_TYPE + "," +
85+
OutcomeEventsTable.COLUMN_NAME_IAM_INFLUENCE_TYPE + TEXT_TYPE + "," +
86+
OutcomeEventsTable.COLUMN_NAME_NOTIFICATION_IDS + TEXT_TYPE + "," +
87+
OutcomeEventsTable.COLUMN_NAME_IAM_IDS + TEXT_TYPE + "," +
88+
OutcomeEventsTable.COLUMN_NAME_NAME + TEXT_TYPE + "," +
89+
OutcomeEventsTable.COLUMN_NAME_TIMESTAMP + TIMESTAMP_TYPE + "," +
90+
OutcomeEventsTable.COLUMN_NAME_WEIGHT + FLOAT_TYPE + "," +
91+
OutcomeEventsTable.COLUMN_NAME_SESSION_TIME + INT_TYPE +
92+
");"
93+
7594
const val SQL_CREATE_UNIQUE_OUTCOME_ENTRIES_V1 = "CREATE TABLE " + CachedUniqueOutcomeTable.TABLE_NAME_V1 + " (" +
7695
CachedUniqueOutcomeTable.ID + INTEGER_PRIMARY_KEY_TYPE + "," +
7796
CachedUniqueOutcomeTable.COLUMN_NAME_NOTIFICATION_ID + TEXT_TYPE + "," +

0 commit comments

Comments
 (0)