mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 10:48:47 +03:00
BIT-1334: Adding generation for catch all email usernames (#538)
This commit is contained in:
parent
36d49a62a6
commit
b6e7655938
10 changed files with 203 additions and 2 deletions
|
@ -26,6 +26,13 @@ interface GeneratorSdkSource {
|
|||
request: UsernameGeneratorRequest.Subaddress,
|
||||
): Result<String>
|
||||
|
||||
/**
|
||||
* Generates a catch all email returning a [String] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun generateCatchAllEmail(
|
||||
request: UsernameGeneratorRequest.Catchall,
|
||||
): Result<String>
|
||||
|
||||
/**
|
||||
* Generates a forwarded service email returning a [String] wrapped in a [Result].
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,12 @@ class GeneratorSdkSourceImpl(
|
|||
clientGenerator.username(request)
|
||||
}
|
||||
|
||||
override suspend fun generateCatchAllEmail(
|
||||
request: UsernameGeneratorRequest.Catchall,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.username(request)
|
||||
}
|
||||
|
||||
override suspend fun generateForwardedServiceEmail(
|
||||
request: UsernameGeneratorRequest.Forwarded,
|
||||
): Result<String> = runCatching {
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.bitwarden.core.PasswordGeneratorRequest
|
|||
import com.bitwarden.core.PasswordHistoryView
|
||||
import com.bitwarden.core.UsernameGeneratorRequest
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.LocalDataState
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedCatchAllUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedForwardedServiceUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
|
@ -40,12 +41,19 @@ interface GeneratorRepository {
|
|||
): GeneratedPassphraseResult
|
||||
|
||||
/**
|
||||
* Attempt to generate a forwarded service username.
|
||||
* Attempt to generate a plus addressed email username.
|
||||
*/
|
||||
suspend fun generatePlusAddressedEmail(
|
||||
plusAddressedEmailGeneratorRequest: UsernameGeneratorRequest.Subaddress,
|
||||
): GeneratedPlusAddressedUsernameResult
|
||||
|
||||
/**
|
||||
* Attempt to generate a catch-all email username.
|
||||
*/
|
||||
suspend fun generateCatchAllEmail(
|
||||
catchAllEmailGeneratorRequest: UsernameGeneratorRequest.Catchall,
|
||||
): GeneratedCatchAllUsernameResult
|
||||
|
||||
/**
|
||||
* Attempt to generate a forwarded service username.
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.x8bit.bitwarden.data.tools.generator.datasource.disk.PasswordHistoryD
|
|||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.entity.toPasswordHistory
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.entity.toPasswordHistoryEntity
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.sdk.GeneratorSdkSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedCatchAllUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedForwardedServiceUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
|
@ -140,6 +141,19 @@ class GeneratorRepositoryImpl(
|
|||
},
|
||||
)
|
||||
|
||||
override suspend fun generateCatchAllEmail(
|
||||
catchAllEmailGeneratorRequest: UsernameGeneratorRequest.Catchall,
|
||||
): GeneratedCatchAllUsernameResult =
|
||||
generatorSdkSource.generateCatchAllEmail(catchAllEmailGeneratorRequest)
|
||||
.fold(
|
||||
onSuccess = { generatedEmail ->
|
||||
GeneratedCatchAllUsernameResult.Success(generatedEmail)
|
||||
},
|
||||
onFailure = {
|
||||
GeneratedCatchAllUsernameResult.InvalidRequest
|
||||
},
|
||||
)
|
||||
|
||||
override suspend fun generateForwardedServiceUsername(
|
||||
forwardedServiceGeneratorRequest: UsernameGeneratorRequest.Forwarded,
|
||||
): GeneratedForwardedServiceUsernameResult =
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.repository.model
|
||||
|
||||
/**
|
||||
* Represents the outcome of a generator operation.
|
||||
*/
|
||||
sealed class GeneratedCatchAllUsernameResult {
|
||||
|
||||
/**
|
||||
* Operation succeeded with a value.
|
||||
*/
|
||||
data class Success(
|
||||
val generatedEmailAddress: String,
|
||||
) : GeneratedCatchAllUsernameResult()
|
||||
|
||||
/**
|
||||
* There was an error during the operation.
|
||||
*/
|
||||
data object InvalidRequest : GeneratedCatchAllUsernameResult()
|
||||
}
|
|
@ -13,6 +13,7 @@ import com.x8bit.bitwarden.R
|
|||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedCatchAllUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedForwardedServiceUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
|
@ -129,6 +130,10 @@ class GeneratorViewModel @Inject constructor(
|
|||
handleUpdatePlusAddressedGeneratedUsernameResult(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.Internal.UpdateGeneratedCatchAllUsernameResult -> {
|
||||
handleUpdateCatchAllGeneratedUsernameResult(action)
|
||||
}
|
||||
|
||||
is GeneratorAction.Internal.UpdateGeneratedForwardedServiceUsernameResult -> {
|
||||
handleUpdateForwadedServiceGeneratedUsernameResult(action)
|
||||
}
|
||||
|
@ -381,6 +386,22 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateCatchAllGeneratedUsernameResult(
|
||||
action: GeneratorAction.Internal.UpdateGeneratedCatchAllUsernameResult,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is GeneratedCatchAllUsernameResult.Success -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(generatedText = result.generatedEmailAddress)
|
||||
}
|
||||
}
|
||||
|
||||
GeneratedCatchAllUsernameResult.InvalidRequest -> {
|
||||
sendEvent(GeneratorEvent.ShowSnackbar(R.string.an_error_has_occurred.asText()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateForwadedServiceGeneratedUsernameResult(
|
||||
action: GeneratorAction.Internal.UpdateGeneratedForwardedServiceUsernameResult,
|
||||
) {
|
||||
|
@ -926,7 +947,9 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is CatchAllEmail -> {
|
||||
// TODO: Implement catch all email generation (BIT-1334)
|
||||
if (isManualRegeneration) {
|
||||
generateCatchAllEmail(selectedType)
|
||||
}
|
||||
}
|
||||
|
||||
is PlusAddressedEmail -> {
|
||||
|
@ -959,6 +982,16 @@ class GeneratorViewModel @Inject constructor(
|
|||
sendAction(GeneratorAction.Internal.UpdateGeneratedPlusAddessedUsernameResult(result))
|
||||
}
|
||||
|
||||
private suspend fun generateCatchAllEmail(catchAllEmail: CatchAllEmail) {
|
||||
val result = generatorRepository.generateCatchAllEmail(
|
||||
UsernameGeneratorRequest.Catchall(
|
||||
type = AppendType.Random,
|
||||
domain = catchAllEmail.domainName,
|
||||
),
|
||||
)
|
||||
sendAction(GeneratorAction.Internal.UpdateGeneratedCatchAllUsernameResult(result))
|
||||
}
|
||||
|
||||
private inline fun updateGeneratorMainTypePasscode(
|
||||
crossinline block: (Passcode) -> Passcode,
|
||||
) {
|
||||
|
@ -1929,6 +1962,13 @@ sealed class GeneratorAction {
|
|||
val result: GeneratedPlusAddressedUsernameResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a generated text update is received.
|
||||
*/
|
||||
data class UpdateGeneratedCatchAllUsernameResult(
|
||||
val result: GeneratedCatchAllUsernameResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a generated text update is received.
|
||||
*/
|
||||
|
|
|
@ -93,6 +93,28 @@ class GeneratorSdkSourceTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `generateCatchAllEmail should call SDK and return a Result with the generated email`() =
|
||||
runBlocking {
|
||||
val request = UsernameGeneratorRequest.Catchall(
|
||||
type = AppendType.Random,
|
||||
domain = "domain",
|
||||
)
|
||||
val expectedResult = "user@domain"
|
||||
|
||||
coEvery {
|
||||
clientGenerators.username(request)
|
||||
} returns expectedResult
|
||||
|
||||
val result = generatorSdkSource.generateCatchAllEmail(request)
|
||||
|
||||
assertEquals(Result.success(expectedResult), result)
|
||||
coVerify {
|
||||
clientGenerators.username(request)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `generateForwardedServiceEmail should call SDK and return a Result with the generated email`() =
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.x8bit.bitwarden.data.tools.generator.datasource.disk.PasswordHistoryD
|
|||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.entity.PasswordHistoryEntity
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.entity.toPasswordHistoryEntity
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.sdk.GeneratorSdkSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedCatchAllUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedForwardedServiceUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
|
@ -303,6 +304,46 @@ class GeneratorRepositoryTest {
|
|||
coVerify { generatorSdkSource.generatePlusAddressedEmail(request) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `generateCatchAllEmail should return Success with generated email when SDK call is successful`() = runTest {
|
||||
val userId = "testUserId"
|
||||
val request = UsernameGeneratorRequest.Catchall(
|
||||
type = AppendType.Random,
|
||||
domain = "domain",
|
||||
)
|
||||
val generatedEmail = "user@domain"
|
||||
|
||||
coEvery { generatorSdkSource.generateCatchAllEmail(request) } returns
|
||||
Result.success(generatedEmail)
|
||||
|
||||
val result = repository.generateCatchAllEmail(request)
|
||||
|
||||
assertEquals(
|
||||
generatedEmail,
|
||||
(result as GeneratedCatchAllUsernameResult.Success).generatedEmailAddress,
|
||||
)
|
||||
coVerify { generatorSdkSource.generateCatchAllEmail(request) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `generateCatchAllEmail should return InvalidRequest on SDK failure`() = runTest {
|
||||
val request = UsernameGeneratorRequest.Catchall(
|
||||
type = AppendType.Random,
|
||||
domain = "user@domain",
|
||||
)
|
||||
val exception = RuntimeException("An error occurred")
|
||||
coEvery {
|
||||
generatorSdkSource.generateCatchAllEmail(request)
|
||||
} returns Result.failure(exception)
|
||||
|
||||
val result = repository.generateCatchAllEmail(request)
|
||||
|
||||
assertTrue(result is GeneratedCatchAllUsernameResult.InvalidRequest)
|
||||
coVerify { generatorSdkSource.generateCatchAllEmail(request) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generateForwardedService should emit Success result and store the generated email`() =
|
||||
runTest {
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.bitwarden.core.PasswordHistoryView
|
|||
import com.bitwarden.core.UsernameGeneratorRequest
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.LocalDataState
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedCatchAllUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedForwardedServiceUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
|
@ -36,6 +37,11 @@ class FakeGeneratorRepository : GeneratorRepository {
|
|||
generatedEmailAddress = "email+abcd1234@address.com",
|
||||
)
|
||||
|
||||
private var generateCatchAllEmailResult: GeneratedCatchAllUsernameResult =
|
||||
GeneratedCatchAllUsernameResult.Success(
|
||||
generatedEmailAddress = "user@domain",
|
||||
)
|
||||
|
||||
private var generateForwardedServiceResult: GeneratedForwardedServiceUsernameResult =
|
||||
GeneratedForwardedServiceUsernameResult.Success(
|
||||
generatedEmailAddress = "updatedUsername",
|
||||
|
@ -63,6 +69,12 @@ class FakeGeneratorRepository : GeneratorRepository {
|
|||
return generatePlusAddressedEmailResult
|
||||
}
|
||||
|
||||
override suspend fun generateCatchAllEmail(
|
||||
catchAllEmailGeneratorRequest: UsernameGeneratorRequest.Catchall,
|
||||
): GeneratedCatchAllUsernameResult {
|
||||
return generateCatchAllEmailResult
|
||||
}
|
||||
|
||||
override suspend fun generateForwardedServiceUsername(
|
||||
forwardedServiceGeneratorRequest: UsernameGeneratorRequest.Forwarded,
|
||||
): GeneratedForwardedServiceUsernameResult {
|
||||
|
@ -122,4 +134,11 @@ class FakeGeneratorRepository : GeneratorRepository {
|
|||
fun setMockGenerateForwardedServiceResult(result: GeneratedForwardedServiceUsernameResult) {
|
||||
generateForwardedServiceResult = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mock result for the generateCatchAll function.
|
||||
*/
|
||||
fun setMockCatchAllResult(result: GeneratedCatchAllUsernameResult) {
|
||||
generateCatchAllEmailResult = result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedCatchAllUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedForwardedServiceUsernameResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
|
@ -235,6 +236,30 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `RegenerateClick for catch all email state should update the catch all email correctly`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel(catchAllEmailSavedStateHandle)
|
||||
|
||||
fakeGeneratorRepository.setMockCatchAllResult(
|
||||
GeneratedCatchAllUsernameResult.Success("DifferentUsername"),
|
||||
)
|
||||
|
||||
viewModel.actionChannel.trySend(GeneratorAction.RegenerateClick)
|
||||
|
||||
val expectedState =
|
||||
initialCatchAllEmailState.copy(
|
||||
generatedText = "DifferentUsername",
|
||||
selectedType = GeneratorState.MainType.Username(
|
||||
GeneratorState.MainType.Username.UsernameType.CatchAllEmail(
|
||||
domainName = "defaultDomain",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CopyClick should call setText on ClipboardManager`() {
|
||||
val viewModel = createViewModel()
|
||||
|
|
Loading…
Add table
Reference in a new issue