Skip to content

Commit b48dc10

Browse files
committed
Fix IAM fetch stuck after login when identity verification is enabled
LoginUserOperationExecutor.createUser() was not passing the RYW token from the backend response to the ConsistencyManager, causing the IamFetchRywTokenKey condition to never resolve after login. This meant InAppMessagesManager coroutines awaited forever and IAMs were never fetched for the logged-in user. - Add rywData field to CreateUserResponse - Parse ryw_token/ryw_delay in JSONConverter.convertToCreateUserResponse - Set RYW data in ConsistencyManager after successful createUser - Fix resolveConditionsWithID to only remove matching conditions - Wrap resolveConditionsWithID in mutex for thread safety
1 parent 89cca43 commit b48dc10

5 files changed

Lines changed: 48 additions & 16 deletions

File tree

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/impl/ConsistencyManager.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,21 @@ class ConsistencyManager : IConsistencyManager {
5656
}
5757

5858
override suspend fun resolveConditionsWithID(id: String) {
59-
val completedConditions = mutableListOf<Pair<ICondition, CompletableDeferred<RywData?>>>()
59+
mutex.withLock {
60+
val completedConditions = mutableListOf<Pair<ICondition, CompletableDeferred<RywData?>>>()
6061

61-
for ((condition, deferred) in conditions) {
62-
if (condition.id == id) {
63-
if (!deferred.isCompleted) {
64-
deferred.complete(null)
62+
for ((condition, deferred) in conditions) {
63+
if (condition.id == id) {
64+
if (!deferred.isCompleted) {
65+
deferred.complete(null)
66+
}
67+
completedConditions.add(Pair(condition, deferred))
6568
}
6669
}
67-
completedConditions.add(Pair(condition, deferred))
68-
}
6970

70-
// Remove completed conditions from the list
71-
conditions.removeAll(completedConditions)
71+
// Remove completed conditions from the list
72+
conditions.removeAll(completedConditions)
73+
}
7274
}
7375

7476
/**

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,8 @@ class CreateUserResponse(
8484
* The subscriptions for the user.
8585
*/
8686
val subscriptions: List<SubscriptionObject>,
87+
/**
88+
* Read-your-write consistency data returned by the backend, if any.
89+
*/
90+
val rywData: RywData? = null,
8791
)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.onesignal.user.internal.backend.impl
22

3+
import com.onesignal.common.consistency.RywData
34
import com.onesignal.common.expandJSONArray
45
import com.onesignal.common.putJSONArray
56
import com.onesignal.common.putMap
@@ -8,6 +9,7 @@ import com.onesignal.common.safeBool
89
import com.onesignal.common.safeDouble
910
import com.onesignal.common.safeInt
1011
import com.onesignal.common.safeJSONObject
12+
import com.onesignal.common.safeLong
1113
import com.onesignal.common.safeString
1214
import com.onesignal.common.toMap
1315
import com.onesignal.user.internal.backend.CreateUserResponse
@@ -55,7 +57,11 @@ object JSONConverter {
5557
return@expandJSONArray null
5658
}
5759

58-
return CreateUserResponse(respIdentities, respProperties, respSubscriptions)
60+
val rywToken = jsonObject.safeString("ryw_token")
61+
val rywDelay = jsonObject.safeLong("ryw_delay")
62+
val rywData = if (rywToken != null) RywData(rywToken, rywDelay) else null
63+
64+
return CreateUserResponse(respIdentities, respProperties, respSubscriptions, rywData)
5965
}
6066

6167
fun convertToJSON(properties: PropertiesObject): JSONObject {

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import com.onesignal.common.NetworkUtils
88
import com.onesignal.common.OneSignalUtils
99
import com.onesignal.common.RootToolsInternalMethods
1010
import com.onesignal.common.TimeUtils
11+
import com.onesignal.common.consistency.IamFetchReadyCondition
12+
import com.onesignal.common.consistency.enums.IamFetchRywTokenKey
13+
import com.onesignal.common.consistency.models.IConsistencyManager
1114
import com.onesignal.common.exceptions.BackendException
1215
import com.onesignal.common.modeling.ModelChangeTags
1316
import com.onesignal.core.internal.application.IApplicationService
@@ -49,6 +52,7 @@ internal class LoginUserOperationExecutor(
4952
private val _configModelStore: ConfigModelStore,
5053
private val _languageContext: ILanguageContext,
5154
private val _jwtTokenStore: JwtTokenStore,
55+
private val _consistencyManager: IConsistencyManager,
5256
) : IOperationExecutor {
5357
override val operations: List<String>
5458
get() = listOf(LOGIN_USER)
@@ -225,6 +229,11 @@ internal class LoginUserOperationExecutor(
225229
backendSubscriptions.remove(backendSubscription)
226230
}
227231

232+
if (response.rywData != null) {
233+
_consistencyManager.setRywData(backendOneSignalId, IamFetchRywTokenKey.USER, response.rywData)
234+
}
235+
_consistencyManager.resolveConditionsWithID(IamFetchReadyCondition.ID)
236+
228237
val wasPossiblyAnUpsert = identities.isNotEmpty()
229238
val followUpOperations =
230239
if (wasPossiblyAnUpsert) {

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.onesignal.user.internal.operations
22

33
import br.com.colman.kotest.android.extensions.robolectric.RobolectricTest
4+
import com.onesignal.common.consistency.models.IConsistencyManager
45
import com.onesignal.common.exceptions.BackendException
56
import com.onesignal.core.internal.operations.ExecutionResponse
67
import com.onesignal.core.internal.operations.ExecutionResult
@@ -79,6 +80,7 @@ class LoginUserOperationExecutorTests : FunSpec({
7980
MockHelper.configModelStore(),
8081
MockHelper.languageContext(),
8182
mockk<JwtTokenStore>(relaxed = true),
83+
mockk<IConsistencyManager>(relaxed = true),
8284
)
8385
val operations =
8486
listOf<Operation>(
@@ -125,6 +127,7 @@ class LoginUserOperationExecutorTests : FunSpec({
125127
MockHelper.configModelStore(),
126128
MockHelper.languageContext(),
127129
mockk<JwtTokenStore>(relaxed = true),
130+
mockk<IConsistencyManager>(relaxed = true),
128131
)
129132
val operations =
130133
listOf<Operation>(
@@ -153,7 +156,7 @@ class LoginUserOperationExecutorTests : FunSpec({
153156
val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
154157

155158
val loginUserOperationExecutor =
156-
LoginUserOperationExecutor(mockIdentityOperationExecutor, AndroidMockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true))
159+
LoginUserOperationExecutor(mockIdentityOperationExecutor, AndroidMockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true), mockk<IConsistencyManager>(relaxed = true))
157160
val operations =
158161
listOf<Operation>(
159162
LoginUserOperation(appId, localOneSignalId, null, null),
@@ -181,7 +184,7 @@ class LoginUserOperationExecutorTests : FunSpec({
181184
val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
182185

183186
val loginUserOperationExecutor =
184-
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true))
187+
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true), mockk<IConsistencyManager>(relaxed = true))
185188
val operations = listOf<Operation>(LoginUserOperation(appId, localOneSignalId, "externalId", null))
186189

187190
// When
@@ -220,6 +223,7 @@ class LoginUserOperationExecutorTests : FunSpec({
220223
MockHelper.configModelStore(),
221224
MockHelper.languageContext(),
222225
mockk<JwtTokenStore>(relaxed = true),
226+
mockk<IConsistencyManager>(relaxed = true),
223227
)
224228
val operations = listOf<Operation>(LoginUserOperation(appId, localOneSignalId, "externalId", null))
225229

@@ -248,7 +252,7 @@ class LoginUserOperationExecutorTests : FunSpec({
248252
val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
249253

250254
val loginUserOperationExecutor =
251-
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true))
255+
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true), mockk<IConsistencyManager>(relaxed = true))
252256
val operations = listOf<Operation>(LoginUserOperation(appId, localOneSignalId, "externalId", "existingOneSignalId"))
253257

254258
// When
@@ -284,7 +288,7 @@ class LoginUserOperationExecutorTests : FunSpec({
284288
val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
285289

286290
val loginUserOperationExecutor =
287-
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true))
291+
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true), mockk<IConsistencyManager>(relaxed = true))
288292
val operations = listOf<Operation>(LoginUserOperation(appId, localOneSignalId, "externalId", "existingOneSignalId"))
289293

290294
// When
@@ -320,7 +324,7 @@ class LoginUserOperationExecutorTests : FunSpec({
320324
val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
321325

322326
val loginUserOperationExecutor =
323-
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true))
327+
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true), mockk<IConsistencyManager>(relaxed = true))
324328
val operations = listOf<Operation>(LoginUserOperation(appId, localOneSignalId, "externalId", "existingOneSignalId"))
325329

326330
// When
@@ -358,7 +362,7 @@ class LoginUserOperationExecutorTests : FunSpec({
358362
val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
359363

360364
val loginUserOperationExecutor =
361-
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true))
365+
LoginUserOperationExecutor(mockIdentityOperationExecutor, MockHelper.applicationService(), MockHelper.deviceService(), mockUserBackendService, mockIdentityModelStore, mockPropertiesModelStore, mockSubscriptionsModelStore, MockHelper.configModelStore(), MockHelper.languageContext(), mockk<JwtTokenStore>(relaxed = true), mockk<IConsistencyManager>(relaxed = true))
362366
val operations = listOf<Operation>(LoginUserOperation(appId, localOneSignalId, "externalId", "existingOneSignalId"))
363367

364368
// When
@@ -410,6 +414,7 @@ class LoginUserOperationExecutorTests : FunSpec({
410414
MockHelper.configModelStore(),
411415
MockHelper.languageContext(),
412416
mockk<JwtTokenStore>(relaxed = true),
417+
mockk<IConsistencyManager>(relaxed = true),
413418
)
414419
val operations =
415420
listOf<Operation>(
@@ -516,6 +521,7 @@ class LoginUserOperationExecutorTests : FunSpec({
516521
MockHelper.configModelStore(),
517522
MockHelper.languageContext(),
518523
mockk<JwtTokenStore>(relaxed = true),
524+
mockk<IConsistencyManager>(relaxed = true),
519525
)
520526
val operations =
521527
listOf<Operation>(
@@ -606,6 +612,7 @@ class LoginUserOperationExecutorTests : FunSpec({
606612
MockHelper.configModelStore(),
607613
MockHelper.languageContext(),
608614
mockk<JwtTokenStore>(relaxed = true),
615+
mockk<IConsistencyManager>(relaxed = true),
609616
)
610617
val operations =
611618
listOf<Operation>(
@@ -682,6 +689,7 @@ class LoginUserOperationExecutorTests : FunSpec({
682689
MockHelper.configModelStore(),
683690
MockHelper.languageContext(),
684691
mockk<JwtTokenStore>(relaxed = true),
692+
mockk<IConsistencyManager>(relaxed = true),
685693
)
686694
val operations =
687695
listOf<Operation>(
@@ -749,6 +757,7 @@ class LoginUserOperationExecutorTests : FunSpec({
749757
MockHelper.configModelStore(),
750758
MockHelper.languageContext(),
751759
mockk<JwtTokenStore>(relaxed = true),
760+
mockk<IConsistencyManager>(relaxed = true),
752761
)
753762
// anonymous Login request
754763
val operations = listOf<Operation>(LoginUserOperation(appId, localOneSignalId, null, null))
@@ -796,6 +805,7 @@ class LoginUserOperationExecutorTests : FunSpec({
796805
MockHelper.configModelStore(),
797806
MockHelper.languageContext(),
798807
mockk<JwtTokenStore>(relaxed = true),
808+
mockk<IConsistencyManager>(relaxed = true),
799809
)
800810

801811
// send PUSH then EMAIL (local IDs 1,2) — order differs from backend response
@@ -861,6 +871,7 @@ class LoginUserOperationExecutorTests : FunSpec({
861871
configModelStore,
862872
MockHelper.languageContext(),
863873
mockk<JwtTokenStore>(relaxed = true),
874+
mockk<IConsistencyManager>(relaxed = true),
864875
)
865876

866877
val ops =

0 commit comments

Comments
 (0)