From 9a50399116f09329a217d4664fbcbde45507d105 Mon Sep 17 00:00:00 2001 From: Patrick Honkonen Date: Fri, 28 Mar 2025 11:20:58 -0400 Subject: [PATCH] Migrate DataStateExtensions to core module Move `DataStateExtensions` to the `core` module. - Deleted `DataStateExtensions.kt` from `authenticator` and `app` modules. - Updated all imports. - Added `junit5` and `kotlinx.coroutines.test` test implementation to `core` module. --- .../processor/Fido2ProviderProcessorImpl.kt | 2 +- .../vault/repository/VaultRepositoryImpl.kt | 8 +- .../feature/send/addsend/AddSendViewModel.kt | 2 +- .../feature/addedit/VaultAddEditViewModel.kt | 2 +- .../vault/feature/item/VaultItemViewModel.kt | 4 +- .../itemlisting/VaultItemListingViewModel.kt | 2 +- .../VaultMoveToOrganizationViewModel.kt | 2 +- .../util/DataStateExtensionsTest.kt | 5 + .../repository/AuthenticatorRepositoryImpl.kt | 4 +- .../repository/util/DataStateExtensions.kt | 94 ------------------- .../feature/edititem/EditItemViewModel.kt | 5 +- .../repository/util/DataStateExtensions.kt | 2 +- 12 files changed, 21 insertions(+), 111 deletions(-) delete mode 100644 authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/util/DataStateExtensions.kt rename {app/src/main/java/com/x8bit/bitwarden/data/platform => core/src/main/kotlin/com/bitwarden/core/data}/repository/util/DataStateExtensions.kt (99%) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt index 1e3998de88..9837abd734 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt @@ -30,6 +30,7 @@ import androidx.credentials.provider.CredentialEntry import androidx.credentials.provider.ProviderClearCredentialStateRequest import androidx.credentials.provider.PublicKeyCredentialEntry import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.takeUntilLoaded import com.bitwarden.fido.Fido2CredentialAutofillView import com.bitwarden.sdk.Fido2CredentialStore import com.bitwarden.vault.CipherView @@ -42,7 +43,6 @@ import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager import com.x8bit.bitwarden.data.platform.manager.model.FlagKey -import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.DecryptFido2CredentialAutofillViewResult diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt index a84bf75b0a..a3e0b02684 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt @@ -6,6 +6,10 @@ import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.repository.util.combineDataStates +import com.bitwarden.core.data.repository.util.map +import com.bitwarden.core.data.repository.util.mapNullable +import com.bitwarden.core.data.repository.util.updateToPendingOrLoading import com.bitwarden.core.data.util.asFailure import com.bitwarden.core.data.util.asSuccess import com.bitwarden.core.data.util.flatMap @@ -38,12 +42,8 @@ import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderDeleteData import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderUpsertData import com.x8bit.bitwarden.data.platform.manager.model.SyncSendDeleteData import com.x8bit.bitwarden.data.platform.manager.model.SyncSendUpsertData -import com.x8bit.bitwarden.data.platform.repository.util.combineDataStates -import com.x8bit.bitwarden.data.platform.repository.util.map -import com.x8bit.bitwarden.data.platform.repository.util.mapNullable import com.x8bit.bitwarden.data.platform.repository.util.observeWhenSubscribedAndLoggedIn import com.x8bit.bitwarden.data.platform.repository.util.observeWhenSubscribedAndUnlocked -import com.x8bit.bitwarden.data.platform.repository.util.updateToPendingOrLoading import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponse import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateSendJsonResponse diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModel.kt index 853b12e302..bc14e603cf 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModel.kt @@ -5,6 +5,7 @@ import android.os.Parcelable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.takeUntilLoaded import com.bitwarden.send.SendView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository @@ -17,7 +18,6 @@ import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManage import com.x8bit.bitwarden.data.platform.manager.util.getActivePolicies import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl -import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt index a2744c01ab..b1ad854203 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.bitwarden.core.DateTime import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.takeUntilLoaded import com.bitwarden.vault.CipherView import com.bitwarden.vault.FolderView import com.x8bit.bitwarden.R @@ -31,7 +32,6 @@ import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrN import com.x8bit.bitwarden.data.platform.manager.util.toFido2CreateRequestOrNull import com.x8bit.bitwarden.data.platform.manager.util.toTotpDataOrNull import com.x8bit.bitwarden.data.platform.repository.SettingsRepository -import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratorResult import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt index 1fa2db1ff2..a9c9318642 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt @@ -5,6 +5,8 @@ import android.os.Parcelable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.combineDataStates +import com.bitwarden.core.data.repository.util.mapNullable import com.bitwarden.vault.CipherView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository @@ -17,8 +19,6 @@ import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl -import com.x8bit.bitwarden.data.platform.repository.util.combineDataStates -import com.x8bit.bitwarden.data.platform.repository.util.mapNullable import com.x8bit.bitwarden.data.vault.manager.FileManager import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt index 20837df99b..3198022f47 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt @@ -5,6 +5,7 @@ import androidx.annotation.DrawableRes import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.map import com.bitwarden.fido.Fido2CredentialAutofillView import com.bitwarden.vault.CipherRepromptType import com.bitwarden.vault.CipherType @@ -43,7 +44,6 @@ import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl -import com.x8bit.bitwarden.data.platform.repository.util.map import com.x8bit.bitwarden.data.platform.util.getFido2RpIdOrNull import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.repository.VaultRepository diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModel.kt index f2bf46f087..3499d2bb28 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModel.kt @@ -4,12 +4,12 @@ import android.os.Parcelable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.combineDataStates import com.bitwarden.vault.CipherView import com.bitwarden.vault.CollectionView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.UserState -import com.x8bit.bitwarden.data.platform.repository.util.combineDataStates import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.ShareCipherResult import com.x8bit.bitwarden.ui.platform.base.BaseViewModel diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/util/DataStateExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/util/DataStateExtensionsTest.kt index 5a83860c56..7d90c87467 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/util/DataStateExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/util/DataStateExtensionsTest.kt @@ -2,6 +2,11 @@ package com.x8bit.bitwarden.data.platform.repository.util import app.cash.turbine.test import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.combineDataStates +import com.bitwarden.core.data.repository.util.map +import com.bitwarden.core.data.repository.util.mapNullable +import com.bitwarden.core.data.repository.util.takeUntilLoaded +import com.bitwarden.core.data.repository.util.updateToPendingOrLoading import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/repository/AuthenticatorRepositoryImpl.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/repository/AuthenticatorRepositoryImpl.kt index aff0eedfaa..f27c05a3e5 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/repository/AuthenticatorRepositoryImpl.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/repository/AuthenticatorRepositoryImpl.kt @@ -23,13 +23,13 @@ import com.bitwarden.authenticator.data.platform.manager.imports.model.ImportDat import com.bitwarden.authenticator.data.platform.manager.imports.model.ImportFileFormat import com.bitwarden.authenticator.data.platform.manager.model.FlagKey import com.bitwarden.authenticator.data.platform.repository.SettingsRepository -import com.bitwarden.core.data.repository.model.DataState -import com.bitwarden.authenticator.data.platform.repository.util.map import com.bitwarden.authenticator.ui.platform.feature.settings.export.model.ExportVaultFormat import com.bitwarden.authenticator.ui.platform.manager.intent.IntentManager import com.bitwarden.authenticatorbridge.manager.AuthenticatorBridgeManager import com.bitwarden.authenticatorbridge.manager.model.AccountSyncState +import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.repository.util.map import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.Channel diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/util/DataStateExtensions.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/util/DataStateExtensions.kt deleted file mode 100644 index fe2baaac98..0000000000 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/util/DataStateExtensions.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.bitwarden.authenticator.data.platform.repository.util - -import com.bitwarden.core.data.repository.model.DataState -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.transformWhile - -/** - * Maps the data inside a [DataState] with the given [transform]. - */ -inline fun DataState.map( - transform: (T) -> R, -): DataState = when (this) { - is DataState.Loaded -> DataState.Loaded(transform(data)) - is DataState.Loading -> DataState.Loading - is DataState.Pending -> DataState.Pending(transform(data)) - is DataState.Error -> DataState.Error(error, data?.let(transform)) - is DataState.NoNetwork -> DataState.NoNetwork(data?.let(transform)) -} - -/** - * Emits all values of a [DataState] [Flow] until it emits a [DataState.Loaded]. - */ -fun Flow>.takeUntilLoaded(): Flow> = transformWhile { - emit(it) - it !is DataState.Loaded -} - -/** - * Combines the [dataState1] and [dataState2] [DataState]s together using the provided [transform]. - * - * This function will internally manage the final `DataState` type that is returned. This is done - * by prioritizing each if the states in this order: - * - * - [DataState.Error] - * - [DataState.NoNetwork] - * - [DataState.Loading] - * - [DataState.Pending] - * - [DataState.Loaded] - * - * This priority order ensures that the total state is accurately reflecting the underlying states. - * If one of the `DataState`s has a higher priority than the other, the output will be the highest - * priority. For example, if one `DataState` is `DataState.Loaded` and another is `DataState.Error` - * then the returned `DataState` will be `DataState.Error`. - * - * The payload of the final `DataState` be created by the `transform` lambda which will be invoked - * whenever the payloads of both `dataState1` and `dataState2` are not null. In the scenario where - * one or both payloads are null, the resulting `DataState` will have a null payload. - */ -fun combineDataStates( - dataState1: DataState, - dataState2: DataState, - transform: (t1: T1, t2: T2) -> R, -): DataState { - // Wraps the `transform` lambda to allow null data to be passed in. If either of the passed in - // values are null, the regular transform will not be invoked and null is returned. - val nullableTransform: (T1?, T2?) -> R? = { t1, t2 -> - if (t1 != null && t2 != null) transform(t1, t2) else null - } - return when { - // Error states have highest priority, fail fast. - dataState1 is DataState.Error -> { - DataState.Error( - error = dataState1.error, - data = nullableTransform(dataState1.data, dataState2.data), - ) - } - - dataState2 is DataState.Error -> { - DataState.Error( - error = dataState2.error, - data = nullableTransform(dataState1.data, dataState2.data), - ) - } - - dataState1 is DataState.NoNetwork || dataState2 is DataState.NoNetwork -> { - DataState.NoNetwork(nullableTransform(dataState1.data, dataState2.data)) - } - - // Something is still loading, we will wait for all the data. - dataState1 is DataState.Loading || dataState2 is DataState.Loading -> DataState.Loading - - // Pending state for everything while any one piece of data is updating. - dataState1 is DataState.Pending || dataState2 is DataState.Pending -> { - @Suppress("UNCHECKED_CAST") - DataState.Pending(transform(dataState1.data as T1, dataState2.data as T2)) - } - - // Both states are Loaded and have data - else -> { - @Suppress("UNCHECKED_CAST") - DataState.Loaded(transform(dataState1.data as T1, dataState2.data as T2)) - } - } -} diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemViewModel.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemViewModel.kt index 7f2fdab628..bc50404376 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemViewModel.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/edititem/EditItemViewModel.kt @@ -11,9 +11,6 @@ import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.Aut import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemType import com.bitwarden.authenticator.data.authenticator.repository.AuthenticatorRepository import com.bitwarden.authenticator.data.authenticator.repository.model.CreateItemResult -import com.bitwarden.core.data.repository.model.DataState -import com.bitwarden.authenticator.data.platform.repository.util.takeUntilLoaded -import com.bitwarden.authenticator.ui.authenticator.feature.edititem.AuthenticatorRefreshPeriodOption.entries import com.bitwarden.authenticator.ui.authenticator.feature.edititem.EditItemState.Companion.MAX_ALLOWED_CODE_DIGITS import com.bitwarden.authenticator.ui.authenticator.feature.edititem.EditItemState.Companion.MIN_ALLOWED_CODE_DIGITS import com.bitwarden.authenticator.ui.authenticator.feature.edititem.model.EditItemData @@ -22,6 +19,8 @@ import com.bitwarden.authenticator.ui.platform.base.util.Text import com.bitwarden.authenticator.ui.platform.base.util.asText import com.bitwarden.authenticator.ui.platform.base.util.concat import com.bitwarden.authenticator.ui.platform.base.util.isBase32 +import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.repository.util.takeUntilLoaded import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/util/DataStateExtensions.kt b/core/src/main/kotlin/com/bitwarden/core/data/repository/util/DataStateExtensions.kt similarity index 99% rename from app/src/main/java/com/x8bit/bitwarden/data/platform/repository/util/DataStateExtensions.kt rename to core/src/main/kotlin/com/bitwarden/core/data/repository/util/DataStateExtensions.kt index 3710b7af92..29429c2fc9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/util/DataStateExtensions.kt +++ b/core/src/main/kotlin/com/bitwarden/core/data/repository/util/DataStateExtensions.kt @@ -1,4 +1,4 @@ -package com.x8bit.bitwarden.data.platform.repository.util +package com.bitwarden.core.data.repository.util import com.bitwarden.core.annotation.OmitFromCoverage import com.bitwarden.core.data.repository.model.DataState