Skip to content

Commit 702d13c

Browse files
authored
Merge pull request #2075 from OneSignal/add-operationRepo-onLoaded-Listener
Follow Up: add operationRepoLoadedListener and implement it to RecoverFromDroppe…
2 parents fda7c3e + 7b20fa5 commit 702d13c

File tree

6 files changed

+100
-2
lines changed

6 files changed

+100
-2
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ interface IOperationRepo {
3838
* Check if the queue contains a specific operation type
3939
*/
4040
fun <T : Operation> containsInstanceOf(type: KClass<T>): Boolean
41+
42+
fun addOperationLoadedListener(handler: IOperationRepoLoadedListener)
4143
}
4244

4345
// Extension function so the syntax containsInstanceOf<Operation>() can be used over
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.onesignal.core.internal.operations
2+
3+
interface IOperationRepoLoadedListener {
4+
fun onOperationRepoLoaded()
5+
}

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.onesignal.core.internal.operations.impl
22

3+
import com.onesignal.common.events.EventProducer
4+
import com.onesignal.common.events.IEventNotifier
35
import com.onesignal.common.threading.WaiterWithValue
46
import com.onesignal.core.internal.config.ConfigModelStore
57
import com.onesignal.core.internal.operations.ExecutionResult
68
import com.onesignal.core.internal.operations.GroupComparisonType
79
import com.onesignal.core.internal.operations.IOperationExecutor
810
import com.onesignal.core.internal.operations.IOperationRepo
11+
import com.onesignal.core.internal.operations.IOperationRepoLoadedListener
912
import com.onesignal.core.internal.operations.Operation
1013
import com.onesignal.core.internal.startup.IStartableService
1114
import com.onesignal.core.internal.time.ITime
@@ -27,7 +30,7 @@ internal class OperationRepo(
2730
private val _configModelStore: ConfigModelStore,
2831
private val _time: ITime,
2932
private val _newRecordState: NewRecordsState,
30-
) : IOperationRepo, IStartableService {
33+
) : IOperationRepo, IStartableService, IEventNotifier<IOperationRepoLoadedListener> {
3134
internal class OperationQueueItem(
3235
val operation: Operation,
3336
val waiter: WaiterWithValue<Boolean>? = null,
@@ -49,6 +52,18 @@ internal class OperationRepo(
4952
private val waiter = WaiterWithValue<LoopWaiterMessage>()
5053
private var paused = false
5154
private var coroutineScope = CoroutineScope(newSingleThreadContext(name = "OpRepo"))
55+
private val loadedSubscription: EventProducer<IOperationRepoLoadedListener> = EventProducer()
56+
57+
override val hasSubscribers: Boolean
58+
get() = loadedSubscription.hasSubscribers
59+
60+
override fun unsubscribe(handler: IOperationRepoLoadedListener) {
61+
loadedSubscription.unsubscribe(handler)
62+
}
63+
64+
override fun subscribe(handler: IOperationRepoLoadedListener) {
65+
loadedSubscription.subscribe(handler)
66+
}
5267

5368
/** *** Buckets ***
5469
* Purpose: Bucketing is a pattern we are using to help save network
@@ -86,6 +101,10 @@ internal class OperationRepo(
86101
}
87102
}
88103

104+
override fun addOperationLoadedListener(handler: IOperationRepoLoadedListener) {
105+
subscribe(handler)
106+
}
107+
89108
override fun start() {
90109
paused = false
91110
coroutineScope.launch {
@@ -393,5 +412,6 @@ internal class OperationRepo(
393412
operation.index,
394413
)
395414
}
415+
loadedSubscription.fire { it.onOperationRepoLoaded() }
396416
}
397417
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/migrations/RecoverFromDroppedLoginBug.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.onesignal.user.internal.migrations
33
import com.onesignal.common.IDManager
44
import com.onesignal.core.internal.config.ConfigModelStore
55
import com.onesignal.core.internal.operations.IOperationRepo
6+
import com.onesignal.core.internal.operations.IOperationRepoLoadedListener
67
import com.onesignal.core.internal.operations.containsInstanceOf
78
import com.onesignal.core.internal.startup.IStartableService
89
import com.onesignal.debug.internal.logging.Logging
@@ -30,8 +31,12 @@ class RecoverFromDroppedLoginBug(
3031
private val _operationRepo: IOperationRepo,
3132
private val _identityModelStore: IdentityModelStore,
3233
private val _configModelStore: ConfigModelStore,
33-
) : IStartableService {
34+
) : IStartableService, IOperationRepoLoadedListener {
3435
override fun start() {
36+
_operationRepo.addOperationLoadedListener(this)
37+
}
38+
39+
override fun onOperationRepoLoaded() {
3540
if (isInBadState()) {
3641
Logging.warn(
3742
"User with externalId:" +

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,24 @@ class OperationRepoTests : FunSpec({
590590
// Then
591591
result shouldBe null
592592
}
593+
594+
test("ensure onOperationRepoLoaded is called once loading is completed") {
595+
// Given
596+
val mocks = Mocks()
597+
val spyListener = spyk<IOperationRepoLoadedListener>()
598+
599+
// When
600+
mocks.operationRepo.addOperationLoadedListener(spyListener)
601+
mocks.operationRepo.start()
602+
603+
// Then
604+
mocks.operationRepo.hasSubscribers shouldBe true
605+
coVerifyOrder {
606+
mocks.operationRepo.subscribe(any())
607+
mocks.operationModelStore.loadOperations()
608+
spyListener.onOperationRepoLoaded()
609+
}
610+
}
593611
}) {
594612
companion object {
595613
private fun mockOperation(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.onesignal.user.internal.migrations
2+
3+
import com.onesignal.core.internal.config.ConfigModelStore
4+
import com.onesignal.core.internal.operations.impl.OperationModelStore
5+
import com.onesignal.core.internal.operations.impl.OperationRepo
6+
import com.onesignal.core.internal.time.impl.Time
7+
import com.onesignal.mocks.MockHelper
8+
import com.onesignal.user.internal.operations.ExecutorMocks
9+
import io.kotest.core.spec.style.FunSpec
10+
import io.mockk.every
11+
import io.mockk.just
12+
import io.mockk.mockk
13+
import io.mockk.runs
14+
import io.mockk.spyk
15+
import io.mockk.verify
16+
17+
class RecoverFromDroppedLoginBugTests : FunSpec({
18+
test("ensure RecoverFromDroppedLoginBug receive onOperationRepoLoaded callback from operationRepo") {
19+
// Given
20+
val mockOperationModelStore = mockk<OperationModelStore>()
21+
val mockConfigModelStore = mockk<ConfigModelStore>()
22+
val operationRepo =
23+
spyk(
24+
OperationRepo(
25+
listOf(),
26+
mockOperationModelStore,
27+
mockConfigModelStore,
28+
Time(),
29+
ExecutorMocks.getNewRecordState(mockConfigModelStore),
30+
),
31+
)
32+
every { mockOperationModelStore.loadOperations() } just runs
33+
every { mockOperationModelStore.list() } returns listOf()
34+
35+
val recovery = RecoverFromDroppedLoginBug(operationRepo, MockHelper.identityModelStore(), mockConfigModelStore)
36+
every { recovery.onOperationRepoLoaded() } just runs
37+
38+
// When
39+
operationRepo.start()
40+
recovery.start()
41+
42+
// Then
43+
verify {
44+
operationRepo.subscribe(recovery)
45+
recovery.onOperationRepoLoaded()
46+
}
47+
}
48+
})

0 commit comments

Comments
 (0)