diff --git a/OneSignalSDK/detekt/detekt-baseline-core.xml b/OneSignalSDK/detekt/detekt-baseline-core.xml
index 3f07ef1a13..88b28052a4 100644
--- a/OneSignalSDK/detekt/detekt-baseline-core.xml
+++ b/OneSignalSDK/detekt/detekt-baseline-core.xml
@@ -42,6 +42,7 @@
ConstructorParameterNaming:InfluenceManager.kt$InfluenceManager$private val _configModelStore: ConfigModelStore
ConstructorParameterNaming:InfluenceManager.kt$InfluenceManager$private val _sessionService: ISessionService
ConstructorParameterNaming:InstallIdService.kt$InstallIdService$private val _prefs: IPreferencesService
+ ConstructorParameterNaming:JwtTokenStore.kt$JwtTokenStore$private val _prefs: IPreferencesService
ConstructorParameterNaming:LanguageContext.kt$LanguageContext$private val _propertiesModelStore: PropertiesModelStore
ConstructorParameterNaming:LoginUserFromSubscriptionOperationExecutor.kt$LoginUserFromSubscriptionOperationExecutor$private val _identityModelStore: IdentityModelStore
ConstructorParameterNaming:LoginUserFromSubscriptionOperationExecutor.kt$LoginUserFromSubscriptionOperationExecutor$private val _propertiesModelStore: PropertiesModelStore
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt
index 9998fd213e..908f55a0d4 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt
@@ -45,6 +45,7 @@ import com.onesignal.location.ILocationManager
import com.onesignal.location.internal.MisconfiguredLocationManager
import com.onesignal.notifications.INotificationsManager
import com.onesignal.notifications.internal.MisconfiguredNotificationsManager
+import com.onesignal.user.internal.jwt.JwtTokenStore
internal class CoreModule : IModule {
override fun register(builder: ServiceBuilder) {
@@ -68,6 +69,8 @@ internal class CoreModule : IModule {
builder.register().provides()
builder.register().provides()
+ builder.register().provides()
+
// Operations
builder.register().provides()
builder.register()
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt
index 616fa1916b..acc89a26ca 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt
@@ -2,6 +2,7 @@ package com.onesignal.core.internal.config
import com.onesignal.common.modeling.Model
import com.onesignal.core.internal.http.OneSignalService.ONESIGNAL_API_BASE_URL
+import com.onesignal.user.internal.jwt.JwtRequirement
import org.json.JSONArray
import org.json.JSONObject
@@ -236,13 +237,18 @@ class ConfigModel : Model() {
setBooleanProperty(::enterprise.name, value)
}
- /**
- * Whether SMS auth hash should be used.
- */
- var useIdentityVerification: Boolean
- get() = getBooleanProperty(::useIdentityVerification.name) { false }
+ /** Mirrors backend `jwt_required`. Pre-HYDRATE callers see [JwtRequirement.UNKNOWN]. */
+ internal var useIdentityVerification: JwtRequirement
+ get() = JwtRequirement.fromBoolean(getOptBooleanProperty(::useIdentityVerification.name))
set(value) {
- setBooleanProperty(::useIdentityVerification.name, value)
+ setOptBooleanProperty(
+ ::useIdentityVerification.name,
+ when (value) {
+ JwtRequirement.UNKNOWN -> null
+ JwtRequirement.NOT_REQUIRED -> false
+ JwtRequirement.REQUIRED -> true
+ },
+ )
}
/**
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt
index e15b6faa07..26d4e1b86e 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt
@@ -10,6 +10,7 @@ import com.onesignal.core.internal.config.ConfigModel
import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.startup.IStartableService
import com.onesignal.debug.internal.logging.Logging
+import com.onesignal.user.internal.jwt.JwtRequirement
import com.onesignal.user.internal.subscriptions.ISubscriptionManager
import kotlinx.coroutines.delay
import java.net.HttpURLConnection
@@ -85,7 +86,9 @@ internal class ConfigModelStoreListener(
// these are only copied from the backend params when the backend has set them.
params.enterprise?.let { config.enterprise = it }
- params.useIdentityVerification?.let { config.useIdentityVerification = it }
+ params.useIdentityVerification?.let {
+ config.useIdentityVerification = JwtRequirement.fromBoolean(it)
+ }
params.firebaseAnalytics?.let { config.firebaseAnalytics = it }
params.restoreTTLFilter?.let { config.restoreTTLFilter = it }
params.clearGroupOnSummaryClick?.let { config.clearGroupOnSummaryClick = it }
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureFlag.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureFlag.kt
index 324421a71a..a2a5b33155 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureFlag.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureFlag.kt
@@ -25,13 +25,17 @@ internal enum class FeatureFlag(
val activationMode: FeatureActivationMode
) {
// Threading mode is selected once per app startup to avoid mixed-mode behavior mid-session.
- //
// Remote key (lowercase) must match backend / Turbine flag id.
- //
SDK_BACKGROUND_THREADING(
"sdk_background_threading",
FeatureActivationMode.APP_STARTUP
),
+
+ /** JWT signing of SDK requests. IMMEDIATE so a kill-switch doesn't need a cold start. */
+ SDK_IDENTITY_VERIFICATION(
+ "sdk_identity_verification",
+ FeatureActivationMode.IMMEDIATE
+ ),
;
fun isEnabledIn(enabledKeys: Set): Boolean {
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureManager.kt
index e9b25c002c..04918d90f7 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureManager.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/features/FeatureManager.kt
@@ -8,6 +8,7 @@ import com.onesignal.core.internal.backend.impl.FeatureFlagsJsonParser
import com.onesignal.core.internal.config.ConfigModel
import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.debug.internal.logging.Logging
+import com.onesignal.user.internal.jwt.IdentityVerificationGates
import kotlinx.serialization.json.JsonObject
internal interface IFeatureManager {
@@ -163,6 +164,13 @@ internal class FeatureManager(
enabled = enabled,
source = "FeatureManager:${feature.activationMode}"
)
+
+ FeatureFlag.SDK_IDENTITY_VERIFICATION ->
+ IdentityVerificationGates.update(
+ featureFlagOn = enabled,
+ jwtRequirement = configModelStore.model.useIdentityVerification,
+ source = "FeatureManager:${feature.activationMode}"
+ )
}
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/Operation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/Operation.kt
index 76f51994ab..708c715d6f 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/Operation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/Operation.kt
@@ -16,6 +16,23 @@ abstract class Operation(name: String) : Model() {
setStringProperty(::name.name, value)
}
+ /**
+ * externalId of the user this operation was enqueued for. Captured at construction
+ * time so the op stays bound to its original user even if the current user changes
+ * before the op executes. Null for anonymous users.
+ */
+ var externalId: String?
+ get() = getOptStringProperty(::externalId.name)
+ internal set(value) { // `internal` so subclass constructors can assign at construction time
+ setOptStringProperty(::externalId.name, value)
+ }
+
+ /**
+ * Whether this operation requires a valid JWT when Identity Verification is active.
+ * Subclasses may override to `false` for endpoints that don't require auth.
+ */
+ open val requiresJwt: Boolean get() = true
+
init {
this.name = name
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/preferences/IPreferencesService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/preferences/IPreferencesService.kt
index f4d4b92a5d..84fab69449 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/preferences/IPreferencesService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/preferences/IPreferencesService.kt
@@ -204,6 +204,9 @@ object PreferenceOneSignalKeys {
*/
const val PREFS_OS_LOCATION_SHARED = "OS_LOCATION_SHARED"
+ /** (String) JSON object mapping externalId -> JWT token. */
+ const val PREFS_OS_JWT_TOKENS = "PREFS_OS_JWT_TOKENS"
+
// Permissions
/**
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/purchases/impl/TrackGooglePurchase.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/purchases/impl/TrackGooglePurchase.kt
index 35f95fac5f..8b2694bcb7 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/purchases/impl/TrackGooglePurchase.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/purchases/impl/TrackGooglePurchase.kt
@@ -240,6 +240,7 @@ internal class TrackGooglePurchase(
TrackPurchaseOperation(
_configModelStore.model.appId,
_identityModelStore.model.onesignalId,
+ _identityModelStore.model.externalId,
newAsExisting,
BigDecimal(0),
purchasesToReport,
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
index 1fcdcd8641..344f7702ed 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
@@ -44,7 +44,14 @@ internal class SessionListener(
override fun onSessionStarted() {
_propertiesModelStore.model.timezone = TimeUtils.getTimeZoneId()
- _operationRepo.enqueue(TrackSessionStartOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId), true)
+ _operationRepo.enqueue(
+ TrackSessionStartOperation(
+ _configModelStore.model.appId,
+ _identityModelStore.model.onesignalId,
+ _identityModelStore.model.externalId,
+ ),
+ true,
+ )
}
override fun onSessionActive() {
@@ -60,7 +67,12 @@ internal class SessionListener(
}
_operationRepo.enqueue(
- TrackSessionEndOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, durationInSeconds),
+ TrackSessionEndOperation(
+ _configModelStore.model.appId,
+ _identityModelStore.model.onesignalId,
+ _identityModelStore.model.externalId,
+ durationInSeconds,
+ ),
)
suspendifyOnIO {
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserSwitcher.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserSwitcher.kt
index 5fba367b1a..f1401e031a 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserSwitcher.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/UserSwitcher.kt
@@ -173,6 +173,7 @@ class UserSwitcher(
LoginUserFromSubscriptionOperation(
configModel.appId,
identityModelStore.model.onesignalId,
+ identityModelStore.model.externalId,
legacyPlayerId,
),
)
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/builduser/impl/RebuildUserService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/builduser/impl/RebuildUserService.kt
index a9f42bcfe1..22442072e5 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/builduser/impl/RebuildUserService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/builduser/impl/RebuildUserService.kt
@@ -52,6 +52,7 @@ class RebuildUserService(
CreateSubscriptionOperation(
appId,
onesignalId,
+ identityModel.externalId,
pushSubscription.id,
pushSubscription.type,
pushSubscription.optedIn,
@@ -60,7 +61,7 @@ class RebuildUserService(
),
)
}
- operations.add(RefreshUserOperation(appId, onesignalId))
+ operations.add(RefreshUserOperation(appId, onesignalId, identityModel.externalId))
return operations
}
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/IJwtUpdateListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/IJwtUpdateListener.kt
new file mode 100644
index 0000000000..63b6a4075d
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/IJwtUpdateListener.kt
@@ -0,0 +1,10 @@
+package com.onesignal.user.internal.jwt
+
+/**
+ * Wake-up notification from [JwtTokenStore] when the JWT for [externalId] changes.
+ * Listeners must call [JwtTokenStore.getJwt] for the current value — event delivery
+ * order is not guaranteed to match mutation order across concurrent writers.
+ */
+internal interface IJwtUpdateListener {
+ fun onJwtUpdated(externalId: String)
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/IdentityVerificationGates.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/IdentityVerificationGates.kt
new file mode 100644
index 0000000000..685777dafd
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/IdentityVerificationGates.kt
@@ -0,0 +1,52 @@
+package com.onesignal.user.internal.jwt
+
+import com.onesignal.debug.internal.logging.Logging
+
+/**
+ * Current Identity Verification gate state, pushed by `FeatureManager.applySideEffects`.
+ *
+ * Stored state is the tri-state [jwtRequirement] — UNKNOWN until the first HYDRATE so consumers
+ * that need to distinguish "we don't know yet" from "customer opted out" can. The Boolean
+ * derivations [ivBehaviorActive] and [newCodePathsRun] cover the common case where consumers
+ * just want yes/no; UNKNOWN reads as `false` for both, which is the safe default before HYDRATE.
+ *
+ * The two gates differ on purpose: [newCodePathsRun] also flips on when our SDK feature flag is
+ * on, honoring rollout state even when the customer hasn't opted in. [ivBehaviorActive] tracks
+ * customer config alone.
+ *
+ * Invariant `ivBehaviorActive == true ⇒ newCodePathsRun == true` is preserved at every
+ * observation because both are derived on read from the stored inputs; a reader can't observe an
+ * inconsistent state.
+ */
+internal object IdentityVerificationGates {
+ @Volatile
+ private var _featureFlagOn: Boolean = false
+
+ /** Customer config (`jwt_required`); [JwtRequirement.UNKNOWN] until the first HYDRATE. */
+ @Volatile
+ var jwtRequirement: JwtRequirement = JwtRequirement.UNKNOWN
+ private set
+
+ /** Whether IV-specific behavior (JWT attachment, auth error handling) applies. UNKNOWN reads as `false`. */
+ val ivBehaviorActive: Boolean
+ get() = jwtRequirement == JwtRequirement.REQUIRED
+
+ /** Whether new IV-related code paths should run. `featureFlag_IV_ON || jwt_required == REQUIRED`. */
+ val newCodePathsRun: Boolean
+ get() = _featureFlagOn || ivBehaviorActive
+
+ /** Idempotent. [source] is logged for traceability. */
+ fun update(
+ featureFlagOn: Boolean,
+ jwtRequirement: JwtRequirement,
+ source: String,
+ ) {
+ _featureFlagOn = featureFlagOn
+ this.jwtRequirement = jwtRequirement
+
+ Logging.debug(
+ "OneSignal: IdentityVerificationGates.update: newCodePathsRun=$newCodePathsRun, " +
+ "ivBehaviorActive=$ivBehaviorActive (source=$source)",
+ )
+ }
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/JwtRequirement.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/JwtRequirement.kt
new file mode 100644
index 0000000000..3f21c4c0f4
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/JwtRequirement.kt
@@ -0,0 +1,31 @@
+package com.onesignal.user.internal.jwt
+
+/**
+ * Customer-side JWT requirement, mirrored from the backend `jwt_required` remote param.
+ * Explicit [UNKNOWN] so callers can distinguish pre-HYDRATE (no value yet) from
+ * [NOT_REQUIRED] (customer opted out).
+ *
+ * Represents only the customer-config side of Identity Verification; do not confuse
+ * with [com.onesignal.core.internal.features.FeatureFlag.SDK_IDENTITY_VERIFICATION] (our
+ * SDK-side rollout switch).
+ */
+internal enum class JwtRequirement {
+ /** Remote params have not been fetched yet. Treat as non-IV until known. */
+ UNKNOWN,
+
+ /** Customer config `jwt_required=false`. No JWT signing. */
+ NOT_REQUIRED,
+
+ /** Customer config `jwt_required=true`. IV-specific behavior active. */
+ REQUIRED,
+ ;
+
+ companion object {
+ fun fromBoolean(value: Boolean?): JwtRequirement =
+ when (value) {
+ null -> UNKNOWN
+ false -> NOT_REQUIRED
+ true -> REQUIRED
+ }
+ }
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/JwtTokenStore.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/JwtTokenStore.kt
new file mode 100644
index 0000000000..102db5e591
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/jwt/JwtTokenStore.kt
@@ -0,0 +1,119 @@
+package com.onesignal.user.internal.jwt
+
+import com.onesignal.common.events.EventProducer
+import com.onesignal.common.events.IEventNotifier
+import com.onesignal.core.internal.preferences.IPreferencesService
+import com.onesignal.core.internal.preferences.PreferenceOneSignalKeys
+import com.onesignal.core.internal.preferences.PreferenceStores
+import com.onesignal.debug.internal.logging.Logging
+import org.json.JSONException
+import org.json.JSONObject
+
+/**
+ * Persistent store mapping externalId -> JWT. Multi-user so ops queued under a previous user
+ * can still resolve their JWT at execution time. Storage is unconditional; *usage* of JWTs is
+ * gated on [IdentityVerificationGates.ivBehaviorActive].
+ */
+internal class JwtTokenStore(
+ private val _prefs: IPreferencesService,
+) : IEventNotifier {
+ private val tokens: MutableMap = mutableMapOf()
+ private var isLoaded: Boolean = false
+ private val updates = EventProducer()
+
+ override val hasSubscribers: Boolean
+ get() = updates.hasSubscribers
+
+ override fun subscribe(handler: IJwtUpdateListener) = updates.subscribe(handler)
+
+ override fun unsubscribe(handler: IJwtUpdateListener) = updates.unsubscribe(handler)
+
+ fun getJwt(externalId: String): String? {
+ synchronized(tokens) {
+ ensureLoaded()
+ return tokens[externalId]
+ }
+ }
+
+ /** Null [jwt] is a no-op; call [invalidateJwt] to remove a token. */
+ fun putJwt(
+ externalId: String,
+ jwt: String?,
+ ) {
+ if (jwt == null) return
+ val changed: Boolean
+ synchronized(tokens) {
+ ensureLoaded()
+ changed = tokens[externalId] != jwt
+ tokens[externalId] = jwt
+ if (changed) {
+ persist()
+ }
+ }
+ if (changed) {
+ updates.fire { it.onJwtUpdated(externalId) }
+ }
+ }
+
+ /** Removes the JWT for [externalId] and notifies subscribers. */
+ fun invalidateJwt(externalId: String) {
+ val existed: Boolean
+ synchronized(tokens) {
+ ensureLoaded()
+ existed = tokens.remove(externalId) != null
+ if (existed) {
+ persist()
+ }
+ }
+ if (existed) {
+ updates.fire { it.onJwtUpdated(externalId) }
+ }
+ }
+
+ /** Drops JWTs whose externalId isn't in [activeIds]. Call on cold start to bound growth. */
+ fun pruneToExternalIds(activeIds: Set) {
+ val removed: Set
+ synchronized(tokens) {
+ ensureLoaded()
+ val toRemove = tokens.keys - activeIds
+ removed = toRemove.toSet()
+ if (removed.isNotEmpty()) {
+ tokens.keys.removeAll(removed)
+ persist()
+ }
+ }
+ for (externalId in removed) {
+ updates.fire { it.onJwtUpdated(externalId) }
+ }
+ }
+
+ /** Caller must hold `synchronized(tokens)`. */
+ private fun ensureLoaded() {
+ if (isLoaded) return
+ val json =
+ _prefs.getString(
+ PreferenceStores.ONESIGNAL,
+ PreferenceOneSignalKeys.PREFS_OS_JWT_TOKENS,
+ )
+ if (json != null) {
+ try {
+ val obj = JSONObject(json)
+ for (key in obj.keys()) {
+ tokens[key] = obj.getString(key)
+ }
+ } catch (e: JSONException) {
+ Logging.warn("JwtTokenStore: failed to parse persisted tokens, starting fresh: ${e.message}")
+ }
+ }
+ isLoaded = true
+ }
+
+ /** Caller must hold `synchronized(tokens)`. */
+ private fun persist() {
+ _prefs.saveString(
+ PreferenceStores.ONESIGNAL,
+ PreferenceOneSignalKeys.PREFS_OS_JWT_TOKENS,
+ JSONObject(tokens.toMap()).toString(),
+ )
+ }
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/CreateSubscriptionOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/CreateSubscriptionOperation.kt
index 35484f670d..c1335a1b25 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/CreateSubscriptionOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/CreateSubscriptionOperation.kt
@@ -88,9 +88,10 @@ class CreateSubscriptionOperation() : Operation(SubscriptionOperationExecutor.CR
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, subscriptionId: String, type: SubscriptionType, enabled: Boolean, address: String, status: SubscriptionStatus) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, subscriptionId: String, type: SubscriptionType, enabled: Boolean, address: String, status: SubscriptionStatus) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.subscriptionId = subscriptionId
this.type = type
this.enabled = enabled
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteAliasOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteAliasOperation.kt
index 1595a6de2b..cfb1bf0bac 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteAliasOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteAliasOperation.kt
@@ -43,9 +43,10 @@ class DeleteAliasOperation() : Operation(IdentityOperationExecutor.DELETE_ALIAS)
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, label: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, label: String) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.label = label
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteSubscriptionOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteSubscriptionOperation.kt
index 14c9aee448..8a5cc6bb52 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteSubscriptionOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteSubscriptionOperation.kt
@@ -44,9 +44,10 @@ class DeleteSubscriptionOperation() : Operation(SubscriptionOperationExecutor.DE
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId) && !IDManager.isLocalId(subscriptionId)
override val applyToRecordId: String get() = subscriptionId
- constructor(appId: String, onesignalId: String, subscriptionId: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, subscriptionId: String) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.subscriptionId = subscriptionId
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteTagOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteTagOperation.kt
index f88ae3c568..4819c3248f 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteTagOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/DeleteTagOperation.kt
@@ -44,9 +44,10 @@ class DeleteTagOperation() : Operation(UpdateUserOperationExecutor.DELETE_TAG) {
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, key: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, key: String) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.key = key
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserFromSubscriptionOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserFromSubscriptionOperation.kt
index 9597283f75..65b26765ce 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserFromSubscriptionOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserFromSubscriptionOperation.kt
@@ -28,7 +28,8 @@ class LoginUserFromSubscriptionOperation() : Operation(LoginUserFromSubscription
}
/**
- * The optional external ID of this newly logged-in user. Must be unique for the [appId].
+ * The subscription ID used to look up the user to log in as. Typically a v4 player ID being
+ * migrated to v5.
*/
var subscriptionId: String
get() = getStringProperty(::subscriptionId.name)
@@ -42,9 +43,10 @@ class LoginUserFromSubscriptionOperation() : Operation(LoginUserFromSubscription
override val canStartExecute: Boolean = true
override val applyToRecordId: String get() = subscriptionId
- constructor(appId: String, onesignalId: String, subscriptionId: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, subscriptionId: String) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.subscriptionId = subscriptionId
}
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserOperation.kt
index ca2f979450..54a20d03f6 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/LoginUserOperation.kt
@@ -32,15 +32,6 @@ class LoginUserOperation() : Operation(LoginUserOperationExecutor.LOGIN_USER) {
setStringProperty(::onesignalId.name, value)
}
- /**
- * The optional external ID of this newly logged-in user. Must be unique for the [appId].
- */
- var externalId: String?
- get() = getOptStringProperty(::externalId.name)
- private set(value) {
- setOptStringProperty(::externalId.name, value)
- }
-
/**
* The user ID of an existing user the [externalId] will be attempted to be associated to first.
* When null (or non-null but unsuccessful), a new user will be upserted. This ID *may* be locally generated
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/RefreshUserOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/RefreshUserOperation.kt
index 953cbe7b9c..5f57cbac79 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/RefreshUserOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/RefreshUserOperation.kt
@@ -35,9 +35,10 @@ class RefreshUserOperation() : Operation(RefreshUserOperationExecutor.REFRESH_US
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
}
override fun translateIds(map: Map) {
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetAliasOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetAliasOperation.kt
index c88c23e46f..3bf5fd678b 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetAliasOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetAliasOperation.kt
@@ -53,9 +53,10 @@ class SetAliasOperation() : Operation(IdentityOperationExecutor.SET_ALIAS) {
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, label: String, value: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, label: String, value: String) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.label = label
this.value = value
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetPropertyOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetPropertyOperation.kt
index 2aff9c174a..0be614b9eb 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetPropertyOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetPropertyOperation.kt
@@ -52,9 +52,10 @@ class SetPropertyOperation() : Operation(UpdateUserOperationExecutor.SET_PROPERT
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, property: String, value: Any?) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, property: String, value: Any?) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.property = property
this.value = value
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetTagOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetTagOperation.kt
index 88bfa06eda..505b79506c 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetTagOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/SetTagOperation.kt
@@ -53,9 +53,10 @@ class SetTagOperation() : Operation(UpdateUserOperationExecutor.SET_TAG) {
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, key: String, value: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, key: String, value: String) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.key = key
this.value = value
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackCustomEventOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackCustomEventOperation.kt
index b510a4fd3f..04956e1877 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackCustomEventOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackCustomEventOperation.kt
@@ -30,15 +30,6 @@ class TrackCustomEventOperation() : Operation(CustomEventOperationExecutor.CUSTO
setStringProperty(::onesignalId.name, value)
}
- /**
- * The optional external ID of current logged-in user. Must be unique for the [appId].
- */
- var externalId: String?
- get() = getOptStringProperty(::externalId.name)
- private set(value) {
- setOptStringProperty(::externalId.name, value)
- }
-
/**
* The timestamp when the custom event was created.
*/
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackPurchaseOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackPurchaseOperation.kt
index 78da8cfb0a..7abb7170be 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackPurchaseOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackPurchaseOperation.kt
@@ -65,9 +65,10 @@ class TrackPurchaseOperation() : Operation(UpdateUserOperationExecutor.TRACK_PUR
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, treatNewAsExisting: Boolean, amountSpent: BigDecimal, purchases: List) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, treatNewAsExisting: Boolean, amountSpent: BigDecimal, purchases: List) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.treatNewAsExisting = treatNewAsExisting
this.amountSpent = amountSpent
this.purchases = purchases
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionEndOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionEndOperation.kt
index 940051e91b..c0dfce68cc 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionEndOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionEndOperation.kt
@@ -43,9 +43,10 @@ class TrackSessionEndOperation() : Operation(UpdateUserOperationExecutor.TRACK_S
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String, sessionTime: Long) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, sessionTime: Long) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.sessionTime = sessionTime
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionStartOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionStartOperation.kt
index e5b9e0f29c..5b1285f5f7 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionStartOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TrackSessionStartOperation.kt
@@ -34,9 +34,10 @@ class TrackSessionStartOperation() : Operation(UpdateUserOperationExecutor.TRACK
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId)
override val applyToRecordId: String get() = onesignalId
- constructor(appId: String, onesignalId: String) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
}
override fun translateIds(map: Map) {
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TransferSubscriptionOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TransferSubscriptionOperation.kt
index 54aa3bae27..c0d227260e 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TransferSubscriptionOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TransferSubscriptionOperation.kt
@@ -50,10 +50,11 @@ class TransferSubscriptionOperation() : Operation(SubscriptionOperationExecutor.
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId) && !IDManager.isLocalId(subscriptionId)
override val applyToRecordId: String get() = subscriptionId
- constructor(appId: String, subscriptionId: String, onesignalId: String) : this() {
+ constructor(appId: String, subscriptionId: String, onesignalId: String, externalId: String?) : this() {
this.appId = appId
this.subscriptionId = subscriptionId
this.onesignalId = onesignalId
+ this.externalId = externalId
}
override fun translateIds(map: Map) {
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/UpdateSubscriptionOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/UpdateSubscriptionOperation.kt
index 57f17a29f5..51ea11282b 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/UpdateSubscriptionOperation.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/UpdateSubscriptionOperation.kt
@@ -87,9 +87,10 @@ class UpdateSubscriptionOperation() : Operation(SubscriptionOperationExecutor.UP
override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId) && !IDManager.isLocalId(subscriptionId)
override val applyToRecordId: String get() = subscriptionId
- constructor(appId: String, onesignalId: String, subscriptionId: String, type: SubscriptionType, enabled: Boolean, address: String, status: SubscriptionStatus) : this() {
+ constructor(appId: String, onesignalId: String, externalId: String?, subscriptionId: String, type: SubscriptionType, enabled: Boolean, address: String, status: SubscriptionStatus) : this() {
this.appId = appId
this.onesignalId = onesignalId
+ this.externalId = externalId
this.subscriptionId = subscriptionId
this.type = type
this.enabled = enabled
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt
index 84093eeccb..2894f52631 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserFromSubscriptionOperationExecutor.kt
@@ -74,7 +74,7 @@ internal class LoginUserFromSubscriptionOperationExecutor(
return ExecutionResponse(
ExecutionResult.SUCCESS,
idTranslations,
- listOf(RefreshUserOperation(loginUserOp.appId, backendOneSignalId)),
+ listOf(RefreshUserOperation(loginUserOp.appId, backendOneSignalId, loginUserOp.externalId)),
)
} catch (ex: BackendException) {
val responseType = NetworkUtils.getResponseStatusType(ex.statusCode)
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt
index 46968b3e71..1d68b9a26b 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt
@@ -89,6 +89,7 @@ internal class LoginUserOperationExecutor(
SetAliasOperation(
loginUserOp.appId,
loginUserOp.existingOnesignalId!!,
+ loginUserOp.externalId,
IdentityConstants.EXTERNAL_ID,
loginUserOp.externalId!!,
),
@@ -223,7 +224,7 @@ internal class LoginUserOperationExecutor(
val wasPossiblyAnUpsert = identities.isNotEmpty()
val followUpOperations =
if (wasPossiblyAnUpsert) {
- listOf(RefreshUserOperation(createUserOperation.appId, backendOneSignalId))
+ listOf(RefreshUserOperation(createUserOperation.appId, backendOneSignalId, createUserOperation.externalId))
} else {
null
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
index 81ab0bb687..e6c82f1a70 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
@@ -220,6 +220,7 @@ internal class SubscriptionOperationExecutor(
CreateSubscriptionOperation(
lastOperation.appId,
lastOperation.onesignalId,
+ lastOperation.externalId,
lastOperation.subscriptionId,
lastOperation.type,
lastOperation.enabled,
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/IdentityModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/IdentityModelStoreListener.kt
index 90a565a5a2..6a45235f1a 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/IdentityModelStoreListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/IdentityModelStoreListener.kt
@@ -27,9 +27,9 @@ internal class IdentityModelStoreListener(
newValue: Any?,
): Operation {
return if (newValue != null && newValue is String) {
- SetAliasOperation(_configModelStore.model.appId, model.onesignalId, property, newValue)
+ SetAliasOperation(_configModelStore.model.appId, model.onesignalId, model.externalId, property, newValue)
} else {
- DeleteAliasOperation(_configModelStore.model.appId, model.onesignalId, property)
+ DeleteAliasOperation(_configModelStore.model.appId, model.onesignalId, model.externalId, property)
}
}
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/PropertiesModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/PropertiesModelStoreListener.kt
index d020c5cc66..ca1b698df7 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/PropertiesModelStoreListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/PropertiesModelStoreListener.kt
@@ -4,6 +4,7 @@ import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.operations.IOperationRepo
import com.onesignal.core.internal.operations.Operation
import com.onesignal.core.internal.operations.listeners.SingletonModelStoreListener
+import com.onesignal.user.internal.identity.IdentityModelStore
import com.onesignal.user.internal.operations.DeleteTagOperation
import com.onesignal.user.internal.operations.SetPropertyOperation
import com.onesignal.user.internal.operations.SetTagOperation
@@ -14,6 +15,7 @@ internal class PropertiesModelStoreListener(
store: PropertiesModelStore,
opRepo: IOperationRepo,
private val _configModelStore: ConfigModelStore,
+ private val _identityModelStore: IdentityModelStore,
) : SingletonModelStoreListener(store, opRepo) {
override fun getReplaceOperation(model: PropertiesModel): Operation? {
// when the property model is replaced, nothing to do on the backend. Already handled via login process.
@@ -36,14 +38,15 @@ internal class PropertiesModelStoreListener(
return null
}
+ val externalId = _identityModelStore.model.externalId
if (path.startsWith(PropertiesModel::tags.name)) {
return if (newValue != null && newValue is String) {
- SetTagOperation(_configModelStore.model.appId, model.onesignalId, property, newValue)
+ SetTagOperation(_configModelStore.model.appId, model.onesignalId, externalId, property, newValue)
} else {
- DeleteTagOperation(_configModelStore.model.appId, model.onesignalId, property)
+ DeleteTagOperation(_configModelStore.model.appId, model.onesignalId, externalId, property)
}
}
- return SetPropertyOperation(_configModelStore.model.appId, model.onesignalId, property, newValue)
+ return SetPropertyOperation(_configModelStore.model.appId, model.onesignalId, externalId, property, newValue)
}
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt
index f0002940e9..a4af81c0f3 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt
@@ -23,6 +23,7 @@ internal class SubscriptionModelStoreListener(
return CreateSubscriptionOperation(
_configModelStore.model.appId,
_identityModelStore.model.onesignalId,
+ _identityModelStore.model.externalId,
model.id,
model.type,
enabledAndStatus.first,
@@ -32,7 +33,12 @@ internal class SubscriptionModelStoreListener(
}
override fun getRemoveOperation(model: SubscriptionModel): Operation {
- return DeleteSubscriptionOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, model.id)
+ return DeleteSubscriptionOperation(
+ _configModelStore.model.appId,
+ _identityModelStore.model.onesignalId,
+ _identityModelStore.model.externalId,
+ model.id,
+ )
}
override fun getUpdateOperation(
@@ -46,6 +52,7 @@ internal class SubscriptionModelStoreListener(
return UpdateSubscriptionOperation(
_configModelStore.model.appId,
_identityModelStore.model.onesignalId,
+ _identityModelStore.model.externalId,
model.id,
model.type,
enabledAndStatus.first,
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/UserRefreshService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/UserRefreshService.kt
index 7b04d7981e..98e6f719db 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/UserRefreshService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/service/UserRefreshService.kt
@@ -32,6 +32,7 @@ class UserRefreshService(
RefreshUserOperation(
_configModelStore.model.appId,
_identityModelStore.model.onesignalId,
+ _identityModelStore.model.externalId,
),
)
}
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureFlagTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureFlagTests.kt
index 54cf41b3a7..358e2ed424 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureFlagTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureFlagTests.kt
@@ -15,4 +15,9 @@ class FeatureFlagTests : FunSpec({
test("SDK_BACKGROUND_THREADING uses the expected remote key") {
FeatureFlag.SDK_BACKGROUND_THREADING.key shouldBe "sdk_background_threading"
}
+
+ test("SDK_IDENTITY_VERIFICATION uses the expected remote key and IMMEDIATE activation") {
+ FeatureFlag.SDK_IDENTITY_VERIFICATION.key shouldBe "sdk_identity_verification"
+ FeatureFlag.SDK_IDENTITY_VERIFICATION.activationMode shouldBe FeatureActivationMode.IMMEDIATE
+ }
})
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureManagerTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureManagerTests.kt
index bc6695d419..9fc1d8301d 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureManagerTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/features/FeatureManagerTests.kt
@@ -4,6 +4,8 @@ import com.onesignal.common.modeling.ModelChangeTags
import com.onesignal.common.threading.ThreadingMode
import com.onesignal.core.internal.config.ConfigModel
import com.onesignal.core.internal.config.ConfigModelStore
+import com.onesignal.user.internal.jwt.IdentityVerificationGates
+import com.onesignal.user.internal.jwt.JwtRequirement
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.mockk.every
@@ -15,11 +17,13 @@ import kotlinx.serialization.json.jsonPrimitive
class FeatureManagerTests : FunSpec({
beforeEach {
ThreadingMode.useBackgroundThreading = false
+ IdentityVerificationGates.update(false, JwtRequirement.UNKNOWN, "test-reset")
}
fun stubConfigModel(model: ConfigModel) {
every { model.sdkRemoteFeatureFlags } returns emptyList()
every { model.sdkRemoteFeatureFlagMetadata } returns null
+ every { model.useIdentityVerification } returns JwtRequirement.UNKNOWN
}
test("initial state enables BACKGROUND_THREADING when key is present in sdk remote flags") {
@@ -188,4 +192,110 @@ class FeatureManagerTests : FunSpec({
manager.enabledFeatureKeys() shouldBe emptyList()
}
+
+ test("initial state: IDENTITY_VERIFICATION flag off + jwt_required=null → gates both false") {
+ val initialModel = mockk()
+ stubConfigModel(initialModel)
+ val configModelStore = mockk()
+ every { configModelStore.model } returns initialModel
+ every { configModelStore.subscribe(any()) } just runs
+
+ val manager = FeatureManager(configModelStore)
+
+ manager.isEnabled(FeatureFlag.SDK_IDENTITY_VERIFICATION) shouldBe false
+ IdentityVerificationGates.newCodePathsRun shouldBe false
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+
+ test("initial state: IDENTITY_VERIFICATION flag on → newCodePathsRun=true, ivBehaviorActive=false") {
+ val initialModel = mockk()
+ stubConfigModel(initialModel)
+ every { initialModel.sdkRemoteFeatureFlags } returns listOf(FeatureFlag.SDK_IDENTITY_VERIFICATION.key)
+ val configModelStore = mockk()
+ every { configModelStore.model } returns initialModel
+ every { configModelStore.subscribe(any()) } just runs
+
+ val manager = FeatureManager(configModelStore)
+
+ manager.isEnabled(FeatureFlag.SDK_IDENTITY_VERIFICATION) shouldBe true
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+
+ test("ERROR STATE: flag off + jwt_required=true → both gates true (customer config wins)") {
+ val initialModel = mockk()
+ stubConfigModel(initialModel)
+ every { initialModel.useIdentityVerification } returns JwtRequirement.REQUIRED
+ val configModelStore = mockk()
+ every { configModelStore.model } returns initialModel
+ every { configModelStore.subscribe(any()) } just runs
+
+ val manager = FeatureManager(configModelStore)
+
+ manager.isEnabled(FeatureFlag.SDK_IDENTITY_VERIFICATION) shouldBe false
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe true
+ }
+
+ test("initial state: flag on + jwt_required=true → full IV (both gates true)") {
+ val initialModel = mockk()
+ stubConfigModel(initialModel)
+ every { initialModel.sdkRemoteFeatureFlags } returns listOf(FeatureFlag.SDK_IDENTITY_VERIFICATION.key)
+ every { initialModel.useIdentityVerification } returns JwtRequirement.REQUIRED
+ val configModelStore = mockk()
+ every { configModelStore.model } returns initialModel
+ every { configModelStore.subscribe(any()) } just runs
+
+ val manager = FeatureManager(configModelStore)
+
+ manager.isEnabled(FeatureFlag.SDK_IDENTITY_VERIFICATION) shouldBe true
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe true
+ }
+
+ test("HYDRATE updates gates when useIdentityVerification arrives as true") {
+ val initialModel = mockk()
+ stubConfigModel(initialModel)
+ val configModelStore = mockk()
+ every { configModelStore.model } returns initialModel
+ every { configModelStore.subscribe(any()) } just runs
+ val manager = FeatureManager(configModelStore)
+
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+
+ val updatedModel = mockk()
+ stubConfigModel(updatedModel)
+ every { updatedModel.useIdentityVerification } returns JwtRequirement.REQUIRED
+ // Match production: store's model is swapped before onModelReplaced fires.
+ every { configModelStore.model } returns updatedModel
+
+ manager.onModelReplaced(updatedModel, ModelChangeTags.HYDRATE)
+
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe true
+ }
+
+ test("IDENTITY_VERIFICATION is IMMEDIATE: mid-session flag flip flows through to the gates") {
+ val initialModel = mockk()
+ stubConfigModel(initialModel)
+ val configModelStore = mockk()
+ every { configModelStore.model } returns initialModel
+ every { configModelStore.subscribe(any()) } just runs
+ val manager = FeatureManager(configModelStore)
+
+ // Mid-session model replacement enables the flag remotely.
+ val updatedModel = mockk()
+ stubConfigModel(updatedModel)
+ every { updatedModel.sdkRemoteFeatureFlags } returns listOf(FeatureFlag.SDK_IDENTITY_VERIFICATION.key)
+ every { updatedModel.useIdentityVerification } returns JwtRequirement.NOT_REQUIRED
+ every { configModelStore.model } returns updatedModel
+
+ manager.onModelReplaced(updatedModel, ModelChangeTags.HYDRATE)
+
+ // Feature flag flips in-memory because IDENTITY_VERIFICATION is IMMEDIATE.
+ manager.isEnabled(FeatureFlag.SDK_IDENTITY_VERIFICATION) shouldBe true
+ // newCodePathsRun reflects the flipped flag; ivBehaviorActive still false (jwt_required=false).
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
})
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationModelStoreTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationModelStoreTests.kt
index d4fcee869d..644b4f4102 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationModelStoreTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationModelStoreTests.kt
@@ -28,7 +28,7 @@ class OperationModelStoreTests : FunSpec({
val jsonArray = JSONArray()
// 1. Create a VALID Operation with onesignalId
- val validOperation = SetPropertyOperation(UUID.randomUUID().toString(), UUID.randomUUID().toString(), "property", "value")
+ val validOperation = SetPropertyOperation(UUID.randomUUID().toString(), UUID.randomUUID().toString(), null, "property", "value")
validOperation.id = UUID.randomUUID().toString()
// 2. Create a VALID operation missing onesignalId
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/jwt/IdentityVerificationGatesTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/jwt/IdentityVerificationGatesTests.kt
new file mode 100644
index 0000000000..d185346ac7
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/jwt/IdentityVerificationGatesTests.kt
@@ -0,0 +1,97 @@
+package com.onesignal.user.internal.jwt
+
+import io.kotest.core.spec.style.FunSpec
+import io.kotest.matchers.shouldBe
+
+class IdentityVerificationGatesTests : FunSpec({
+ // Singleton state leaks across tests; reset before each.
+ beforeEach {
+ IdentityVerificationGates.update(false, JwtRequirement.UNKNOWN, "test-reset")
+ }
+
+ test("defaults to newCodePathsRun=false and ivBehaviorActive=false") {
+ IdentityVerificationGates.newCodePathsRun shouldBe false
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+
+ test("featureFlagOn=false, jwtRequirement=UNKNOWN: both gates are false (safe default)") {
+ IdentityVerificationGates.update(
+ featureFlagOn = false,
+ jwtRequirement = JwtRequirement.UNKNOWN,
+ source = "test",
+ )
+ IdentityVerificationGates.newCodePathsRun shouldBe false
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+
+ test("featureFlagOn=false, jwtRequirement=NOT_REQUIRED: both gates are false") {
+ IdentityVerificationGates.update(
+ featureFlagOn = false,
+ jwtRequirement = JwtRequirement.NOT_REQUIRED,
+ source = "test",
+ )
+ IdentityVerificationGates.newCodePathsRun shouldBe false
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+
+ test("ERROR STATE — featureFlagOn=false, jwtRequirement=REQUIRED: both gates true (customer config wins)") {
+ IdentityVerificationGates.update(
+ featureFlagOn = false,
+ jwtRequirement = JwtRequirement.REQUIRED,
+ source = "test",
+ )
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe true
+ }
+
+ test("featureFlagOn=true, jwtRequirement=UNKNOWN: newCodePathsRun true, ivBehaviorActive false") {
+ IdentityVerificationGates.update(
+ featureFlagOn = true,
+ jwtRequirement = JwtRequirement.UNKNOWN,
+ source = "test",
+ )
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+
+ test("featureFlagOn=true, jwtRequirement=NOT_REQUIRED: newCodePathsRun true, ivBehaviorActive false (Phase 3)") {
+ IdentityVerificationGates.update(
+ featureFlagOn = true,
+ jwtRequirement = JwtRequirement.NOT_REQUIRED,
+ source = "test",
+ )
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+
+ test("featureFlagOn=true, jwtRequirement=REQUIRED: both gates true (full IV)") {
+ IdentityVerificationGates.update(
+ featureFlagOn = true,
+ jwtRequirement = JwtRequirement.REQUIRED,
+ source = "test",
+ )
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe true
+ }
+
+ test("updating to the same values is a no-op but still reflects in reads") {
+ IdentityVerificationGates.update(true, JwtRequirement.REQUIRED, "first")
+ IdentityVerificationGates.update(true, JwtRequirement.REQUIRED, "second")
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe true
+ }
+
+ test("transition: non-IV → IV-active → off") {
+ IdentityVerificationGates.update(false, JwtRequirement.NOT_REQUIRED, "phase-1-non-iv")
+ IdentityVerificationGates.newCodePathsRun shouldBe false
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+
+ IdentityVerificationGates.update(true, JwtRequirement.REQUIRED, "phase-2-iv-on")
+ IdentityVerificationGates.newCodePathsRun shouldBe true
+ IdentityVerificationGates.ivBehaviorActive shouldBe true
+
+ IdentityVerificationGates.update(false, JwtRequirement.NOT_REQUIRED, "kill-switch")
+ IdentityVerificationGates.newCodePathsRun shouldBe false
+ IdentityVerificationGates.ivBehaviorActive shouldBe false
+ }
+})
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/jwt/JwtTokenStoreTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/jwt/JwtTokenStoreTests.kt
new file mode 100644
index 0000000000..664e502349
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/jwt/JwtTokenStoreTests.kt
@@ -0,0 +1,231 @@
+package com.onesignal.user.internal.jwt
+
+import com.onesignal.core.internal.preferences.PreferenceOneSignalKeys
+import com.onesignal.core.internal.preferences.PreferenceStores
+import com.onesignal.debug.LogLevel
+import com.onesignal.debug.internal.logging.Logging
+import com.onesignal.mocks.MockPreferencesService
+import io.kotest.core.spec.style.FunSpec
+import io.kotest.matchers.shouldBe
+import org.json.JSONObject
+
+class JwtTokenStoreTests : FunSpec({
+ beforeEach {
+ // Silence logging to avoid android.util.Log.w not-mocked failures in Logging.warn
+ Logging.logLevel = LogLevel.NONE
+ }
+
+ test("getJwt returns null for an externalId never stored") {
+ val store = JwtTokenStore(MockPreferencesService())
+
+ store.getJwt("alice") shouldBe null
+ }
+
+ test("putJwt stores a token retrievable by externalId") {
+ val store = JwtTokenStore(MockPreferencesService())
+
+ store.putJwt("alice", "token-a")
+
+ store.getJwt("alice") shouldBe "token-a"
+ }
+
+ test("putJwt replaces an existing token for the same externalId") {
+ val store = JwtTokenStore(MockPreferencesService())
+ store.putJwt("alice", "token-a1")
+
+ store.putJwt("alice", "token-a2")
+
+ store.getJwt("alice") shouldBe "token-a2"
+ }
+
+ test("putJwt with null is a no-op (invalidate is the explicit path)") {
+ val store = JwtTokenStore(MockPreferencesService())
+ store.putJwt("alice", "token-a")
+
+ store.putJwt("alice", null)
+
+ store.getJwt("alice") shouldBe "token-a"
+ }
+
+ test("invalidateJwt removes the token for externalId") {
+ val store = JwtTokenStore(MockPreferencesService())
+ store.putJwt("alice", "token-a")
+
+ store.invalidateJwt("alice")
+
+ store.getJwt("alice") shouldBe null
+ }
+
+ test("invalidateJwt on an absent externalId is a no-op (no crash)") {
+ val store = JwtTokenStore(MockPreferencesService())
+
+ store.invalidateJwt("alice")
+
+ store.getJwt("alice") shouldBe null
+ }
+
+ test("putJwt persists to preferences and can be recovered by a fresh store instance") {
+ val prefs = MockPreferencesService()
+ val first = JwtTokenStore(prefs)
+ first.putJwt("alice", "token-a")
+ first.putJwt("bob", "token-b")
+
+ val second = JwtTokenStore(prefs)
+
+ second.getJwt("alice") shouldBe "token-a"
+ second.getJwt("bob") shouldBe "token-b"
+ }
+
+ test("invalidateJwt persists so next launch does not see the token") {
+ val prefs = MockPreferencesService()
+ val first = JwtTokenStore(prefs)
+ first.putJwt("alice", "token-a")
+ first.invalidateJwt("alice")
+
+ val second = JwtTokenStore(prefs)
+
+ second.getJwt("alice") shouldBe null
+ }
+
+ test("pruneToExternalIds removes tokens whose externalId is not in the active set") {
+ val store = JwtTokenStore(MockPreferencesService())
+ store.putJwt("alice", "token-a")
+ store.putJwt("bob", "token-b")
+ store.putJwt("chris", "token-c")
+
+ store.pruneToExternalIds(setOf("alice", "chris"))
+
+ store.getJwt("alice") shouldBe "token-a"
+ store.getJwt("bob") shouldBe null
+ store.getJwt("chris") shouldBe "token-c"
+ }
+
+ test("subscribers are notified when a new JWT is put") {
+ val store = JwtTokenStore(MockPreferencesService())
+ val calls = mutableListOf()
+ store.subscribe(
+ object : IJwtUpdateListener {
+ override fun onJwtUpdated(externalId: String) {
+ calls.add(externalId)
+ }
+ },
+ )
+
+ store.putJwt("alice", "token-a")
+
+ calls shouldBe listOf("alice")
+ }
+
+ test("subscribers are NOT notified when putJwt does not change the stored token") {
+ val store = JwtTokenStore(MockPreferencesService())
+ store.putJwt("alice", "token-a")
+ val calls = mutableListOf()
+ store.subscribe(
+ object : IJwtUpdateListener {
+ override fun onJwtUpdated(externalId: String) {
+ calls.add(externalId)
+ }
+ },
+ )
+
+ store.putJwt("alice", "token-a")
+
+ calls.isEmpty() shouldBe true
+ }
+
+ test("subscribers are notified on invalidation") {
+ val store = JwtTokenStore(MockPreferencesService())
+ store.putJwt("alice", "token-a")
+ val calls = mutableListOf()
+ store.subscribe(
+ object : IJwtUpdateListener {
+ override fun onJwtUpdated(externalId: String) {
+ calls.add(externalId)
+ }
+ },
+ )
+
+ store.invalidateJwt("alice")
+
+ calls shouldBe listOf("alice")
+ }
+
+ test("subscribers are NOT notified when invalidating a non-existent token") {
+ val store = JwtTokenStore(MockPreferencesService())
+ val calls = mutableListOf()
+ store.subscribe(
+ object : IJwtUpdateListener {
+ override fun onJwtUpdated(externalId: String) {
+ calls.add(externalId)
+ }
+ },
+ )
+
+ store.invalidateJwt("alice")
+
+ calls.isEmpty() shouldBe true
+ }
+
+ test("pruneToExternalIds fires for each removed externalId") {
+ val store = JwtTokenStore(MockPreferencesService())
+ store.putJwt("alice", "token-a")
+ store.putJwt("bob", "token-b")
+ store.putJwt("chris", "token-c")
+ val calls = mutableListOf()
+ store.subscribe(
+ object : IJwtUpdateListener {
+ override fun onJwtUpdated(externalId: String) {
+ calls.add(externalId)
+ }
+ },
+ )
+
+ store.pruneToExternalIds(setOf("alice"))
+
+ // Order is not deterministic across JVMs; check set semantics.
+ calls.toSet() shouldBe setOf("bob", "chris")
+ }
+
+ test("unsubscribed listener is not notified") {
+ val store = JwtTokenStore(MockPreferencesService())
+ val calls = mutableListOf()
+ val listener =
+ object : IJwtUpdateListener {
+ override fun onJwtUpdated(externalId: String) {
+ calls.add(externalId)
+ }
+ }
+ store.subscribe(listener)
+ store.unsubscribe(listener)
+
+ store.putJwt("alice", "token-a")
+
+ calls.isEmpty() shouldBe true
+ }
+
+ test("persisted JSON is the expected shape") {
+ val prefs = MockPreferencesService()
+ val store = JwtTokenStore(prefs)
+
+ store.putJwt("alice", "token-a")
+ store.putJwt("bob", "token-b")
+
+ val raw = prefs.getString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.PREFS_OS_JWT_TOKENS)
+ val obj = JSONObject(requireNotNull(raw))
+ obj.getString("alice") shouldBe "token-a"
+ obj.getString("bob") shouldBe "token-b"
+ }
+
+ test("malformed persisted JSON starts fresh without crashing") {
+ val prefs =
+ MockPreferencesService(
+ mapOf(PreferenceOneSignalKeys.PREFS_OS_JWT_TOKENS to "{not valid json"),
+ )
+ val store = JwtTokenStore(prefs)
+
+ store.getJwt("alice") shouldBe null
+ // Can still store new tokens after a malformed load
+ store.putJwt("alice", "token-a")
+ store.getJwt("alice") shouldBe "token-a"
+ }
+})
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt
index 34d0681c48..057ef2c96a 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/IdentityOperationExecutorTests.kt
@@ -40,7 +40,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(SetAliasOperation("appId", "onesignalId", "aliasKey1", "aliasValue1"))
+ val operations = listOf(SetAliasOperation("appId", "onesignalId", null, "aliasKey1", "aliasValue1"))
// When
val response = identityOperationExecutor.execute(operations)
@@ -70,7 +70,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(SetAliasOperation("appId", "onesignalId", "aliasKey1", "aliasValue1"))
+ val operations = listOf(SetAliasOperation("appId", "onesignalId", null, "aliasKey1", "aliasValue1"))
// When
@@ -91,7 +91,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(SetAliasOperation("appId", "onesignalId", "aliasKey1", "aliasValue1"))
+ val operations = listOf(SetAliasOperation("appId", "onesignalId", null, "aliasKey1", "aliasValue1"))
// When
@@ -112,7 +112,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(SetAliasOperation("appId", "onesignalId", "aliasKey1", "aliasValue1"))
+ val operations = listOf(SetAliasOperation("appId", "onesignalId", null, "aliasKey1", "aliasValue1"))
// When
@@ -135,7 +135,7 @@ class IdentityOperationExecutorTests : FunSpec({
val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add("onesignalId") }
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, newRecordState)
- val operations = listOf(SetAliasOperation("appId", "onesignalId", "aliasKey1", "aliasValue1"))
+ val operations = listOf(SetAliasOperation("appId", "onesignalId", null, "aliasKey1", "aliasValue1"))
// When
@@ -161,7 +161,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(DeleteAliasOperation("appId", "onesignalId", "aliasKey1"))
+ val operations = listOf(DeleteAliasOperation("appId", "onesignalId", null, "aliasKey1"))
// When
val response = identityOperationExecutor.execute(operations)
@@ -184,7 +184,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(DeleteAliasOperation("appId", "onesignalId", "aliasKey1"))
+ val operations = listOf(DeleteAliasOperation("appId", "onesignalId", null, "aliasKey1"))
// When
@@ -204,7 +204,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(DeleteAliasOperation("appId", "onesignalId", "aliasKey1"))
+ val operations = listOf(DeleteAliasOperation("appId", "onesignalId", null, "aliasKey1"))
// When
@@ -226,7 +226,7 @@ class IdentityOperationExecutorTests : FunSpec({
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, getNewRecordState())
- val operations = listOf(DeleteAliasOperation("appId", "onesignalId", "aliasKey1"))
+ val operations = listOf(DeleteAliasOperation("appId", "onesignalId", null, "aliasKey1"))
// When
@@ -251,7 +251,7 @@ class IdentityOperationExecutorTests : FunSpec({
val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add("onesignalId") }
val identityOperationExecutor =
IdentityOperationExecutor(mockIdentityBackendService, mockIdentityModelStore, mockBuildUserService, newRecordState)
- val operations = listOf(DeleteAliasOperation("appId", "onesignalId", "aliasKey1"))
+ val operations = listOf(DeleteAliasOperation("appId", "onesignalId", null, "aliasKey1"))
// When
val response = identityOperationExecutor.execute(operations)
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/LoginUserOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/LoginUserOperationExecutorTests.kt
index d80dc5531a..e3c8c1cfb4 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/LoginUserOperationExecutorTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/LoginUserOperationExecutorTests.kt
@@ -43,6 +43,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
"subscriptionId1",
SubscriptionType.PUSH,
true,
@@ -410,6 +411,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
"subscriptionId1",
SubscriptionType.PUSH,
true,
@@ -419,6 +421,7 @@ class LoginUserOperationExecutorTests : FunSpec({
UpdateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
"subscriptionId1",
SubscriptionType.PUSH,
true,
@@ -428,13 +431,14 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
"subscriptionId2",
SubscriptionType.EMAIL,
true,
"name@company.com",
SubscriptionStatus.SUBSCRIBED,
),
- DeleteSubscriptionOperation(appId, localOneSignalId, "subscriptionId2"),
+ DeleteSubscriptionOperation(appId, localOneSignalId, null, "subscriptionId2"),
)
// When
@@ -511,6 +515,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
localSubscriptionId1,
SubscriptionType.PUSH,
true,
@@ -520,6 +525,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
localSubscriptionId2,
SubscriptionType.EMAIL,
true,
@@ -597,6 +603,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
localSubscriptionId1,
SubscriptionType.PUSH,
true,
@@ -606,6 +613,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
localSubscriptionId2,
SubscriptionType.EMAIL,
true,
@@ -669,6 +677,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
localSubscriptionId1,
SubscriptionType.PUSH,
true,
@@ -678,6 +687,7 @@ class LoginUserOperationExecutorTests : FunSpec({
CreateSubscriptionOperation(
appId,
localOneSignalId,
+ null,
localSubscriptionId2,
SubscriptionType.EMAIL,
true,
@@ -777,8 +787,8 @@ class LoginUserOperationExecutorTests : FunSpec({
val ops =
listOf(
LoginUserOperation(appId, localOneSignalId, null, null),
- CreateSubscriptionOperation(appId, localOneSignalId, localSubscriptionId1, SubscriptionType.PUSH, true, "pushToken2", SubscriptionStatus.SUBSCRIBED),
- CreateSubscriptionOperation(appId, localOneSignalId, localSubscriptionId2, SubscriptionType.EMAIL, true, "name@company.com", SubscriptionStatus.SUBSCRIBED),
+ CreateSubscriptionOperation(appId, localOneSignalId, null, localSubscriptionId1, SubscriptionType.PUSH, true, "pushToken2", SubscriptionStatus.SUBSCRIBED),
+ CreateSubscriptionOperation(appId, localOneSignalId, null, localSubscriptionId2, SubscriptionType.EMAIL, true, "name@company.com", SubscriptionStatus.SUBSCRIBED),
)
// When
@@ -840,7 +850,7 @@ class LoginUserOperationExecutorTests : FunSpec({
val ops =
listOf(
LoginUserOperation(appId, localOneSignalId, null, null),
- CreateSubscriptionOperation(appId, localOneSignalId, localSubscriptionId1, SubscriptionType.PUSH, true, "pushToken1", SubscriptionStatus.SUBSCRIBED),
+ CreateSubscriptionOperation(appId, localOneSignalId, null, localSubscriptionId1, SubscriptionType.PUSH, true, "pushToken1", SubscriptionStatus.SUBSCRIBED),
)
// When
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/RefreshUserOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/RefreshUserOperationExecutorTests.kt
index 2689d761af..0f3765c557 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/RefreshUserOperationExecutorTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/RefreshUserOperationExecutorTests.kt
@@ -109,7 +109,7 @@ class RefreshUserOperationExecutorTests : FunSpec({
getNewRecordState(),
)
- val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId))
+ val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId, null))
try {
// When
@@ -193,7 +193,7 @@ class RefreshUserOperationExecutorTests : FunSpec({
getNewRecordState(),
)
- val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId))
+ val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId, null))
// When
val response = refreshUserOperationExecutor.execute(operations)
@@ -232,7 +232,7 @@ class RefreshUserOperationExecutorTests : FunSpec({
getNewRecordState(),
)
- val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId))
+ val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId, null))
// When
val response = refreshUserOperationExecutor.execute(operations)
@@ -267,7 +267,7 @@ class RefreshUserOperationExecutorTests : FunSpec({
getNewRecordState(),
)
- val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId))
+ val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId, null))
// When
val response = refreshUserOperationExecutor.execute(operations)
@@ -302,7 +302,7 @@ class RefreshUserOperationExecutorTests : FunSpec({
getNewRecordState(),
)
- val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId))
+ val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId, null))
// When
val response = refreshUserOperationExecutor.execute(operations)
@@ -339,7 +339,7 @@ class RefreshUserOperationExecutorTests : FunSpec({
newRecordState,
)
- val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId))
+ val operations = listOf(RefreshUserOperation(appId, remoteOneSignalId, null))
// When
val response = refreshUserOperationExecutor.execute(operations)
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt
index 4ae3053247..78f8ffc47f 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt
@@ -75,6 +75,7 @@ class SubscriptionOperationExecutorTests :
CreateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
localSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -135,6 +136,7 @@ class SubscriptionOperationExecutorTests :
CreateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
remoteSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -185,6 +187,7 @@ class SubscriptionOperationExecutorTests :
CreateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
localSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -240,6 +243,7 @@ class SubscriptionOperationExecutorTests :
CreateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
localSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -295,6 +299,7 @@ class SubscriptionOperationExecutorTests :
CreateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
localSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -338,13 +343,14 @@ class SubscriptionOperationExecutorTests :
CreateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
localSubscriptionId,
SubscriptionType.PUSH,
true,
"pushToken",
SubscriptionStatus.SUBSCRIBED,
),
- DeleteSubscriptionOperation(appId, remoteOneSignalId, localSubscriptionId),
+ DeleteSubscriptionOperation(appId, remoteOneSignalId, null, localSubscriptionId),
)
// When
@@ -384,6 +390,7 @@ class SubscriptionOperationExecutorTests :
CreateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
localSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -393,6 +400,7 @@ class SubscriptionOperationExecutorTests :
UpdateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
localSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -454,6 +462,7 @@ class SubscriptionOperationExecutorTests :
UpdateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
remoteSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -463,6 +472,7 @@ class SubscriptionOperationExecutorTests :
UpdateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
remoteSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -515,6 +525,7 @@ class SubscriptionOperationExecutorTests :
UpdateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
remoteSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -567,6 +578,7 @@ class SubscriptionOperationExecutorTests :
UpdateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
remoteSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -621,6 +633,7 @@ class SubscriptionOperationExecutorTests :
UpdateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
remoteSubscriptionId,
SubscriptionType.PUSH,
true,
@@ -660,7 +673,7 @@ class SubscriptionOperationExecutorTests :
val operations =
listOf(
- DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+ DeleteSubscriptionOperation(appId, remoteOneSignalId, null, remoteSubscriptionId),
)
// When
@@ -694,7 +707,7 @@ class SubscriptionOperationExecutorTests :
val operations =
listOf(
- DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+ DeleteSubscriptionOperation(appId, remoteOneSignalId, null, remoteSubscriptionId),
)
// When
@@ -729,7 +742,7 @@ class SubscriptionOperationExecutorTests :
val operations =
listOf(
- DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+ DeleteSubscriptionOperation(appId, remoteOneSignalId, null, remoteSubscriptionId),
)
// When
@@ -764,7 +777,7 @@ class SubscriptionOperationExecutorTests :
val operations =
listOf(
- DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+ DeleteSubscriptionOperation(appId, remoteOneSignalId, null, remoteSubscriptionId),
)
// When
@@ -808,6 +821,7 @@ class SubscriptionOperationExecutorTests :
UpdateSubscriptionOperation(
appId,
remoteOneSignalId,
+ null,
remoteSubscriptionId,
SubscriptionType.PUSH,
true,
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt
index de2e148ff8..2e0c122cbc 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt
@@ -57,7 +57,7 @@ class UpdateUserOperationExecutorTests :
getNewRecordState(),
mockConsistencyManager,
)
- val operations = listOf(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
+ val operations = listOf(SetTagOperation(appId, remoteOneSignalId, null, "tagKey1", "tagValue1"))
// When
val response = loginUserOperationExecutor.execute(operations)
@@ -99,21 +99,21 @@ class UpdateUserOperationExecutorTests :
)
val operations =
listOf(
- SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1-1"),
- SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1-2"),
- SetTagOperation(appId, remoteOneSignalId, "tagKey2", "tagValue2"),
- SetTagOperation(appId, remoteOneSignalId, "tagKey3", "tagValue3"),
- DeleteTagOperation(appId, remoteOneSignalId, "tagKey3"),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::language.name, "lang1"),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::language.name, "lang2"),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::timezone.name, "timezone"),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::country.name, "country"),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationLatitude.name, 123.45),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationLongitude.name, 678.90),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationType.name, 1),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationAccuracy.name, 0.15),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationBackground.name, true),
- SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationTimestamp.name, 1111L),
+ SetTagOperation(appId, remoteOneSignalId, null, "tagKey1", "tagValue1-1"),
+ SetTagOperation(appId, remoteOneSignalId, null, "tagKey1", "tagValue1-2"),
+ SetTagOperation(appId, remoteOneSignalId, null, "tagKey2", "tagValue2"),
+ SetTagOperation(appId, remoteOneSignalId, null, "tagKey3", "tagValue3"),
+ DeleteTagOperation(appId, remoteOneSignalId, null, "tagKey3"),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::language.name, "lang1"),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::language.name, "lang2"),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::timezone.name, "timezone"),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::country.name, "country"),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::locationLatitude.name, 123.45),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::locationLongitude.name, 678.90),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::locationType.name, 1),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::locationAccuracy.name, 0.15),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::locationBackground.name, true),
+ SetPropertyOperation(appId, localOneSignalId, null, PropertiesModel::locationTimestamp.name, 1111L),
)
// When
@@ -161,7 +161,7 @@ class UpdateUserOperationExecutorTests :
)
val operations =
listOf(
- TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
+ TrackSessionEndOperation(appId, remoteOneSignalId, null, 1111),
)
// When
@@ -206,10 +206,11 @@ class UpdateUserOperationExecutorTests :
)
val operations =
listOf(
- TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
+ TrackSessionEndOperation(appId, remoteOneSignalId, null, 1111),
TrackPurchaseOperation(
appId,
remoteOneSignalId,
+ null,
false,
BigDecimal(2222),
listOf(
@@ -217,7 +218,7 @@ class UpdateUserOperationExecutorTests :
PurchaseInfo("sku2", "iso2", BigDecimal(1222)),
),
),
- TrackSessionEndOperation(appId, remoteOneSignalId, 3333),
+ TrackSessionEndOperation(appId, remoteOneSignalId, null, 3333),
)
// When
@@ -271,9 +272,9 @@ class UpdateUserOperationExecutorTests :
)
val operations =
listOf(
- TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
- SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"),
- TrackSessionEndOperation(appId, remoteOneSignalId, 3333),
+ TrackSessionEndOperation(appId, remoteOneSignalId, null, 1111),
+ SetTagOperation(appId, remoteOneSignalId, null, "tagKey1", "tagValue1"),
+ TrackSessionEndOperation(appId, remoteOneSignalId, null, 3333),
)
// When
@@ -317,7 +318,7 @@ class UpdateUserOperationExecutorTests :
getNewRecordState(),
mockConsistencyManager,
)
- val operations = listOf(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
+ val operations = listOf(SetTagOperation(appId, remoteOneSignalId, null, "tagKey1", "tagValue1"))
// When
val response = loginUserOperationExecutor.execute(operations)
@@ -350,7 +351,7 @@ class UpdateUserOperationExecutorTests :
newRecordState,
mockConsistencyManager,
)
- val operations = listOf(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
+ val operations = listOf(SetTagOperation(appId, remoteOneSignalId, null, "tagKey1", "tagValue1"))
// When
val response = loginUserOperationExecutor.execute(operations)
@@ -383,7 +384,7 @@ class UpdateUserOperationExecutorTests :
val operations =
listOf(
- TrackSessionStartOperation(appId, onesignalId = remoteOneSignalId),
+ TrackSessionStartOperation(appId, remoteOneSignalId, null),
)
// When
diff --git a/OneSignalSDK/onesignal/notifications/src/test/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListenerTests.kt b/OneSignalSDK/onesignal/notifications/src/test/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListenerTests.kt
index 188cc66cbd..3009177772 100644
--- a/OneSignalSDK/onesignal/notifications/src/test/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListenerTests.kt
+++ b/OneSignalSDK/onesignal/notifications/src/test/java/com/onesignal/notifications/internal/listeners/DeviceRegistrationListenerTests.kt
@@ -252,7 +252,7 @@ class DeviceRegistrationListenerTests : FunSpec({
permission = true,
pushModel = uninitializedPushModel(),
pushTokenResponse =
- PushTokenResponse(NEW_TOKEN, SubscriptionStatus.SUBSCRIBED),
+ PushTokenResponse(NEW_TOKEN, SubscriptionStatus.SUBSCRIBED),
)
// Permission flips off between gate evaluation and the IO callback.
every { harness.notificationsManager.permission } returns true andThen false