Add wrapper for dispatchers (#183)

This commit is contained in:
David Perez 2023-10-31 10:02:00 -05:00 committed by Álison Fernandes
parent b31b859516
commit 3c8a0893fd
15 changed files with 204 additions and 51 deletions

View file

@ -19,9 +19,9 @@ import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
import com.x8bit.bitwarden.data.auth.repository.util.toUserState
import com.x8bit.bitwarden.data.auth.util.toSdkParams
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.util.asSuccess
import com.x8bit.bitwarden.data.platform.util.flatMap
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@ -46,9 +46,9 @@ class AuthRepositoryImpl @Inject constructor(
private val identityService: IdentityService,
private val authSdkSource: AuthSdkSource,
private val authDiskSource: AuthDiskSource,
dispatcher: CoroutineDispatcher,
dispatcherManager: DispatcherManager,
) : AuthRepository {
private val scope = CoroutineScope(dispatcher)
private val scope = CoroutineScope(dispatcherManager.io)
override val authStateFlow: StateFlow<AuthState> = authDiskSource
.userStateFlow

View file

@ -7,11 +7,11 @@ import com.x8bit.bitwarden.data.auth.datasource.network.service.IdentityService
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.Dispatchers
import javax.inject.Singleton
/**
@ -30,12 +30,13 @@ object AuthRepositoryModule {
haveIBeenPwnedService: HaveIBeenPwnedService,
authSdkSource: AuthSdkSource,
authDiskSource: AuthDiskSource,
dispatchers: DispatcherManager,
): AuthRepository = AuthRepositoryImpl(
accountsService = accountsService,
identityService = identityService,
authSdkSource = authSdkSource,
authDiskSource = authDiskSource,
haveIBeenPwnedService = haveIBeenPwnedService,
dispatcher = Dispatchers.IO,
dispatcherManager = dispatchers,
)
}

View file

@ -4,8 +4,8 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.AuthState
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -18,10 +18,10 @@ class NetworkConfigManagerImpl(
private val authTokenInterceptor: AuthTokenInterceptor,
private val environmentRepository: EnvironmentRepository,
private val baseUrlInterceptors: BaseUrlInterceptors,
dispatcher: CoroutineDispatcher,
dispatcherManager: DispatcherManager,
) : NetworkConfigManager {
private val scope = CoroutineScope(dispatcher)
private val scope = CoroutineScope(dispatcherManager.io)
init {
authRepository

View file

@ -0,0 +1,44 @@
package com.x8bit.bitwarden.data.platform.manager.di
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManagerImpl
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManagerImpl
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
/**
* Provides repositories in the auth package.
*/
@Module
@InstallIn(SingletonComponent::class)
object PlatformManagerModule {
@Provides
@Singleton
fun provideBitwardenDispatchers(): DispatcherManager = DispatcherManagerImpl()
@Provides
@Singleton
fun provideNetworkConfigManager(
authRepository: AuthRepository,
authTokenInterceptor: AuthTokenInterceptor,
environmentRepository: EnvironmentRepository,
baseUrlInterceptors: BaseUrlInterceptors,
dispatcherManager: DispatcherManager,
): NetworkConfigManager =
NetworkConfigManagerImpl(
authRepository = authRepository,
authTokenInterceptor = authTokenInterceptor,
environmentRepository = environmentRepository,
baseUrlInterceptors = baseUrlInterceptors,
dispatcherManager = dispatcherManager,
)
}

View file

@ -0,0 +1,29 @@
package com.x8bit.bitwarden.data.platform.manager.dispatcher
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.MainCoroutineDispatcher
/**
* An interface for accessing the [CoroutineDispatcher]s.
*/
interface DispatcherManager {
/**
* The default [CoroutineDispatcher] for the app.
*/
val default: CoroutineDispatcher
/**
* The [MainCoroutineDispatcher] for the app.
*/
val main: MainCoroutineDispatcher
/**
* The IO [CoroutineDispatcher] for the app.
*/
val io: CoroutineDispatcher
/**
* The unconfined [CoroutineDispatcher] for the app.
*/
val unconfined: CoroutineDispatcher
}

View file

@ -0,0 +1,18 @@
package com.x8bit.bitwarden.data.platform.manager.dispatcher
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainCoroutineDispatcher
/**
* Primary implementation of [DispatcherManager].
*/
class DispatcherManagerImpl : DispatcherManager {
override val default: CoroutineDispatcher = Dispatchers.Default
override val main: MainCoroutineDispatcher = Dispatchers.Main
override val io: CoroutineDispatcher = Dispatchers.IO
override val unconfined: CoroutineDispatcher = Dispatchers.Unconfined
}

View file

@ -2,9 +2,9 @@ package com.x8bit.bitwarden.data.platform.repository
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrls
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@ -16,10 +16,10 @@ import kotlinx.coroutines.flow.stateIn
*/
class EnvironmentRepositoryImpl(
private val environmentDiskSource: EnvironmentDiskSource,
private val dispatcher: CoroutineDispatcher,
dispatcherManager: DispatcherManager,
) : EnvironmentRepository {
private val scope = CoroutineScope(dispatcher)
private val scope = CoroutineScope(dispatcherManager.io)
override var environment: Environment
get() = environmentDiskSource

View file

@ -1,18 +1,13 @@
package com.x8bit.bitwarden.data.platform.repository.di
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManagerImpl
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepositoryImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.Dispatchers
import javax.inject.Singleton
/**
@ -26,25 +21,10 @@ object PlatformRepositoryModule {
@Singleton
fun provideEnvironmentRepository(
environmentDiskSource: EnvironmentDiskSource,
dispatcherManager: DispatcherManager,
): EnvironmentRepository =
EnvironmentRepositoryImpl(
environmentDiskSource = environmentDiskSource,
dispatcher = Dispatchers.IO,
)
@Provides
@Singleton
fun provideNetworkConfigManager(
authRepository: AuthRepository,
authTokenInterceptor: AuthTokenInterceptor,
environmentRepository: EnvironmentRepository,
baseUrlInterceptors: BaseUrlInterceptors,
): NetworkConfigManager =
NetworkConfigManagerImpl(
authRepository = authRepository,
authTokenInterceptor = authTokenInterceptor,
environmentRepository = environmentRepository,
baseUrlInterceptors = baseUrlInterceptors,
dispatcher = Dispatchers.IO,
dispatcherManager = dispatcherManager,
)
}

View file

@ -1,10 +1,10 @@
package com.x8bit.bitwarden.data.vault.repository
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipherList
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -15,10 +15,10 @@ import kotlinx.coroutines.launch
class VaultRepositoryImpl constructor(
private val syncService: SyncService,
private val vaultSdkSource: VaultSdkSource,
dispatcher: CoroutineDispatcher,
dispatcherManager: DispatcherManager,
) : VaultRepository {
private val scope = CoroutineScope(dispatcher)
private val scope = CoroutineScope(dispatcherManager.io)
private var syncJob: Job = Job().apply { complete() }

View file

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.data.vault.repository.di
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
@ -8,7 +9,6 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.Dispatchers
import javax.inject.Singleton
/**
@ -23,9 +23,10 @@ class VaultRepositoryModule {
fun providesVaultRepository(
syncService: SyncService,
vaultSdkSource: VaultSdkSource,
dispatcherManager: DispatcherManager,
): VaultRepository = VaultRepositoryImpl(
syncService = syncService,
vaultSdkSource = vaultSdkSource,
dispatcher = Dispatchers.IO,
dispatcherManager = dispatcherManager,
)
}

View file

@ -28,6 +28,8 @@ import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
import com.x8bit.bitwarden.data.auth.repository.util.toUserState
import com.x8bit.bitwarden.data.auth.util.toSdkParams
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.util.asSuccess
import io.mockk.clearMocks
import io.mockk.coEvery
@ -36,11 +38,9 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
@ -50,6 +50,7 @@ import org.junit.jupiter.api.Test
class AuthRepositoryTest {
private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
private val accountsService: AccountsService = mockk()
private val identityService: IdentityService = mockk()
private val haveIBeenPwnedService: HaveIBeenPwnedService = mockk()
@ -80,14 +81,13 @@ class AuthRepositoryTest {
)
}
@OptIn(ExperimentalCoroutinesApi::class)
private val repository = AuthRepositoryImpl(
accountsService = accountsService,
identityService = identityService,
haveIBeenPwnedService = haveIBeenPwnedService,
authSdkSource = authSdkSource,
authDiskSource = fakeAuthDiskSource,
dispatcher = UnconfinedTestDispatcher(),
dispatcherManager = dispatcherManager,
)
@BeforeEach

View file

@ -0,0 +1,39 @@
package com.x8bit.bitwarden.data.platform.base
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
/**
* A faked implementation of [DispatcherManager] that uses [UnconfinedTestDispatcher].
*/
@OptIn(ExperimentalCoroutinesApi::class)
class FakeDispatcherManager : DispatcherManager {
override val default: CoroutineDispatcher = UnconfinedTestDispatcher()
override val main: MainCoroutineDispatcher = Dispatchers.Main
override val io: CoroutineDispatcher = UnconfinedTestDispatcher()
override val unconfined: CoroutineDispatcher = UnconfinedTestDispatcher()
/**
* Updates the main dispatcher to use the provided [dispatcher]. Used in conjunction with
* [resetMain].
*/
fun setMain(dispatcher: CoroutineDispatcher) {
Dispatchers.setMain(dispatcher)
}
/**
* Restores the main dispatcher to it's default state. Used in conjunction with [setMain].
*/
fun resetMain() {
Dispatchers.resetMain()
}
}

View file

@ -2,22 +2,23 @@ package com.x8bit.bitwarden.data.platform.manager
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.AuthState
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@OptIn(ExperimentalCoroutinesApi::class)
class NetworkConfigManagerTest {
private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
private val mutableAuthStateFlow = MutableStateFlow<AuthState>(AuthState.Uninitialized)
private val mutableEnvironmentStateFlow = MutableStateFlow<Environment>(Environment.Us)
@ -41,7 +42,7 @@ class NetworkConfigManagerTest {
authTokenInterceptor = authTokenInterceptor,
environmentRepository = environmentRepository,
baseUrlInterceptors = baseUrlInterceptors,
dispatcher = UnconfinedTestDispatcher(),
dispatcherManager = dispatcherManager,
)
}

View file

@ -0,0 +1,38 @@
package com.x8bit.bitwarden.data.platform.manager.dispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class DispatcherManagerTest {
private val dispatcherManager: DispatcherManager = DispatcherManagerImpl()
@Test
fun `io should return Dispatchers IO`() = runTest {
val expected = Dispatchers.IO
val actual = dispatcherManager.io
assertEquals(expected, actual)
}
@Test
fun `main should return Dispatchers Main`() = runTest {
val expected = Dispatchers.Main
val actual = dispatcherManager.main
assertEquals(expected, actual)
}
@Test
fun `default should return Dispatchers Default`() = runTest {
val expected = Dispatchers.Default
val actual = dispatcherManager.default
assertEquals(expected, actual)
}
}

View file

@ -2,18 +2,18 @@ package com.x8bit.bitwarden.data.platform.repository
import app.cash.turbine.test
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrls
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
@ -22,12 +22,14 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class EnvironmentRepositoryTest {
private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
private val fakeEnvironmentDiskSource = FakeEnvironmentDiskSource()
@OptIn(ExperimentalCoroutinesApi::class)
private val repository = EnvironmentRepositoryImpl(
environmentDiskSource = fakeEnvironmentDiskSource,
dispatcher = UnconfinedTestDispatcher(),
dispatcherManager = dispatcherManager,
)
@BeforeEach