Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ interface IOperationRepo {
* Check if the queue contains a specific operation type
*/
fun <T : Operation> containsInstanceOf(type: KClass<T>): Boolean

fun addOperationLoadedListener(handler: IOperationRepoLoadedListener)
}

// Extension function so the syntax containsInstanceOf<Operation>() can be used over
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.onesignal.core.internal.operations

interface IOperationRepoLoadedListener {
fun onOperationRepoLoaded()
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.onesignal.core.internal.operations.impl

import com.onesignal.common.events.EventProducer
import com.onesignal.common.events.IEventNotifier
import com.onesignal.common.threading.WaiterWithValue
import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.operations.ExecutionResult
import com.onesignal.core.internal.operations.GroupComparisonType
import com.onesignal.core.internal.operations.IOperationExecutor
import com.onesignal.core.internal.operations.IOperationRepo
import com.onesignal.core.internal.operations.IOperationRepoLoadedListener
import com.onesignal.core.internal.operations.Operation
import com.onesignal.core.internal.startup.IStartableService
import com.onesignal.core.internal.time.ITime
Expand All @@ -27,7 +30,7 @@ internal class OperationRepo(
private val _configModelStore: ConfigModelStore,
private val _time: ITime,
private val _newRecordState: NewRecordsState,
) : IOperationRepo, IStartableService {
) : IOperationRepo, IStartableService, IEventNotifier<IOperationRepoLoadedListener> {
internal class OperationQueueItem(
val operation: Operation,
val waiter: WaiterWithValue<Boolean>? = null,
Expand All @@ -49,6 +52,18 @@ internal class OperationRepo(
private val waiter = WaiterWithValue<LoopWaiterMessage>()
private var paused = false
private var coroutineScope = CoroutineScope(newSingleThreadContext(name = "OpRepo"))
private val loadedSubscription: EventProducer<IOperationRepoLoadedListener> = EventProducer()

override val hasSubscribers: Boolean
get() = loadedSubscription.hasSubscribers

override fun unsubscribe(handler: IOperationRepoLoadedListener) {
loadedSubscription.unsubscribe(handler)
}

override fun subscribe(handler: IOperationRepoLoadedListener) {
loadedSubscription.subscribe(handler)
}

/** *** Buckets ***
* Purpose: Bucketing is a pattern we are using to help save network
Expand Down Expand Up @@ -86,6 +101,10 @@ internal class OperationRepo(
}
}

override fun addOperationLoadedListener(handler: IOperationRepoLoadedListener) {
subscribe(handler)
}

override fun start() {
paused = false
coroutineScope.launch {
Expand Down Expand Up @@ -393,5 +412,6 @@ internal class OperationRepo(
operation.index,
)
}
loadedSubscription.fire { it.onOperationRepoLoaded() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.onesignal.user.internal.migrations
import com.onesignal.common.IDManager
import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.operations.IOperationRepo
import com.onesignal.core.internal.operations.IOperationRepoLoadedListener
import com.onesignal.core.internal.operations.containsInstanceOf
import com.onesignal.core.internal.startup.IStartableService
import com.onesignal.debug.internal.logging.Logging
Expand Down Expand Up @@ -30,8 +31,12 @@ class RecoverFromDroppedLoginBug(
private val _operationRepo: IOperationRepo,
private val _identityModelStore: IdentityModelStore,
private val _configModelStore: ConfigModelStore,
) : IStartableService {
) : IStartableService, IOperationRepoLoadedListener {
override fun start() {
_operationRepo.addOperationLoadedListener(this)
}

override fun onOperationRepoLoaded() {
if (isInBadState()) {
Logging.warn(
"User with externalId:" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,24 @@ class OperationRepoTests : FunSpec({
// Then
result shouldBe null
}

test("ensure onOperationRepoLoaded is called once loading is completed") {
// Given
val mocks = Mocks()
val spyListener = spyk<IOperationRepoLoadedListener>()

// When
mocks.operationRepo.addOperationLoadedListener(spyListener)
mocks.operationRepo.start()

// Then
mocks.operationRepo.hasSubscribers shouldBe true
coVerifyOrder {
mocks.operationRepo.subscribe(any())
mocks.operationModelStore.loadOperations()
spyListener.onOperationRepoLoaded()
}
}
}) {
companion object {
private fun mockOperation(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.onesignal.user.internal.migrations

import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.operations.impl.OperationModelStore
import com.onesignal.core.internal.operations.impl.OperationRepo
import com.onesignal.core.internal.time.impl.Time
import com.onesignal.mocks.MockHelper
import com.onesignal.user.internal.operations.ExecutorMocks
import io.kotest.core.spec.style.FunSpec
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import io.mockk.spyk
import io.mockk.verify

class RecoverFromDroppedLoginBugTests : FunSpec({
test("ensure RecoverFromDroppedLoginBug receive onOperationRepoLoaded callback from operationRepo") {
// Given
val mockOperationModelStore = mockk<OperationModelStore>()
val mockConfigModelStore = mockk<ConfigModelStore>()
val operationRepo =
spyk(
OperationRepo(
listOf(),
mockOperationModelStore,
mockConfigModelStore,
Time(),
ExecutorMocks.getNewRecordState(mockConfigModelStore),
),
)
every { mockOperationModelStore.loadOperations() } just runs
every { mockOperationModelStore.list() } returns listOf()

val recovery = RecoverFromDroppedLoginBug(operationRepo, MockHelper.identityModelStore(), mockConfigModelStore)
every { recovery.onOperationRepoLoaded() } just runs

// When
operationRepo.start()
recovery.start()

// Then
verify {
operationRepo.subscribe(recovery)
recovery.onOperationRepoLoaded()
}
}
})