Add ConfigurationApproval validation
This commit is contained in:
parent
9f96ab6ee0
commit
d04a91f566
12 changed files with 187 additions and 7 deletions
|
@ -18,6 +18,10 @@ internal interface DomainContract {
|
|||
fun execute(password: String): ValidationResult
|
||||
}
|
||||
|
||||
fun interface ValidateConfigurationApproval {
|
||||
fun execute(isApproved: Boolean?, isAutoDiscoveryTrusted: Boolean?): ValidationResult
|
||||
}
|
||||
|
||||
fun interface ValidateServer {
|
||||
fun execute(server: String): ValidationResult
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package app.k9mail.feature.account.setup.domain.usecase
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationError
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
|
||||
|
||||
class ValidateConfigurationApproval : UseCase.ValidateConfigurationApproval {
|
||||
override fun execute(isApproved: Boolean?, isAutoDiscoveryTrusted: Boolean?): ValidationResult {
|
||||
return if (isApproved == null && isAutoDiscoveryTrusted == null) {
|
||||
ValidationResult.Success
|
||||
} else if (isAutoDiscoveryTrusted == true) {
|
||||
ValidationResult.Success
|
||||
} else if (isApproved == true) {
|
||||
ValidationResult.Success
|
||||
} else {
|
||||
ValidationResult.Failure(ValidateConfigurationApprovalError.ApprovalRequired)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ValidateConfigurationApprovalError : ValidationError {
|
||||
object ApprovalRequired : ValidateConfigurationApprovalError
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ interface AccountAutoConfigContract {
|
|||
interface Validator {
|
||||
fun validateEmailAddress(emailAddress: String): ValidationResult
|
||||
fun validatePassword(password: String): ValidationResult
|
||||
fun validateConfigurationApproval(isApproved: Boolean?, isAutoDiscoveryTrusted: Boolean?): ValidationResult
|
||||
}
|
||||
|
||||
sealed interface Error {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package app.k9mail.feature.account.setup.ui.autoconfig
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateConfigurationApproval
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailAddress
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword
|
||||
|
||||
internal class AccountAutoConfigValidator(
|
||||
private val emailAddressValidator: ValidateEmailAddress = ValidateEmailAddress(),
|
||||
private val passwordValidator: ValidatePassword = ValidatePassword(),
|
||||
private val emailAddressValidator: UseCase.ValidateEmailAddress = ValidateEmailAddress(),
|
||||
private val passwordValidator: UseCase.ValidatePassword = ValidatePassword(),
|
||||
private val configurationApprovalValidator: UseCase.ValidateConfigurationApproval = ValidateConfigurationApproval(),
|
||||
) : AccountAutoConfigContract.Validator {
|
||||
|
||||
override fun validateEmailAddress(emailAddress: String): ValidationResult {
|
||||
|
@ -16,4 +19,11 @@ internal class AccountAutoConfigValidator(
|
|||
override fun validatePassword(password: String): ValidationResult {
|
||||
return passwordValidator.execute(password)
|
||||
}
|
||||
|
||||
override fun validateConfigurationApproval(
|
||||
isApproved: Boolean?,
|
||||
isAutoDiscoveryTrusted: Boolean?,
|
||||
): ValidationResult {
|
||||
return configurationApprovalValidator.execute(isApproved, isAutoDiscoveryTrusted)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,13 +149,23 @@ internal class AccountAutoConfigViewModel(
|
|||
with(state.value) {
|
||||
val emailValidationResult = validator.validateEmailAddress(emailAddress.value)
|
||||
val passwordValidationResult = validator.validatePassword(password.value)
|
||||
val hasError = listOf(emailValidationResult, passwordValidationResult)
|
||||
.any { it is ValidationResult.Failure }
|
||||
val configurationApprovalValidationResult = validator.validateConfigurationApproval(
|
||||
isApproved = configurationApproved.value,
|
||||
isAutoDiscoveryTrusted = autoDiscoverySettings?.isTrusted,
|
||||
)
|
||||
val hasError = listOf(
|
||||
emailValidationResult,
|
||||
passwordValidationResult,
|
||||
configurationApprovalValidationResult,
|
||||
).any { it is ValidationResult.Failure }
|
||||
|
||||
updateState {
|
||||
it.copy(
|
||||
emailAddress = it.emailAddress.updateFromValidationResult(emailValidationResult),
|
||||
password = it.password.updateFromValidationResult(passwordValidationResult),
|
||||
configurationApproved = it.configurationApproved.updateFromValidationResult(
|
||||
configurationApprovalValidationResult,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package app.k9mail.feature.account.setup.ui.autoconfig
|
||||
|
||||
import android.content.res.Resources
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationError
|
||||
import app.k9mail.feature.account.setup.R
|
||||
import app.k9mail.feature.account.setup.domain.entity.AutoDiscoveryConnectionSecurity
|
||||
import app.k9mail.feature.account.setup.domain.usecase.ValidateConfigurationApproval
|
||||
|
||||
internal fun AutoDiscoveryConnectionSecurity.toResourceString(resources: Resources): String {
|
||||
return when (this) {
|
||||
AutoDiscoveryConnectionSecurity.StartTLS -> resources.getString(
|
||||
R.string.account_setup_connection_security_start_tls,
|
||||
)
|
||||
|
||||
AutoDiscoveryConnectionSecurity.TLS -> resources.getString(
|
||||
R.string.account_setup_connection_security_ssl,
|
||||
)
|
||||
|
@ -21,3 +24,23 @@ internal fun AccountAutoConfigContract.Error.toResourceString(resources: Resourc
|
|||
AccountAutoConfigContract.Error.UnknownError -> resources.getString(R.string.account_setup_error_unknown)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ValidationError.toResourceString(resources: Resources): String {
|
||||
return when (this) {
|
||||
is ValidateConfigurationApproval.ValidateConfigurationApprovalError -> toConfigurationApprovalErrorString(
|
||||
resources,
|
||||
)
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown error: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun ValidateConfigurationApproval.ValidateConfigurationApprovalError.toConfigurationApprovalErrorString(
|
||||
resources: Resources,
|
||||
): String {
|
||||
return when (this) {
|
||||
ValidateConfigurationApproval.ValidateConfigurationApprovalError.ApprovalRequired -> resources.getString(
|
||||
R.string.account_setup_error_configuration_approval_required,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ internal fun AutoDiscoveryServerSettingsView(
|
|||
},
|
||||
)
|
||||
|
||||
if (username != null) {
|
||||
if (username.isNotEmpty()) {
|
||||
ServerSettingRow(
|
||||
icon = Icons.Filled.user,
|
||||
text = buildAnnotatedString {
|
||||
|
|
|
@ -5,17 +5,21 @@ import androidx.compose.foundation.layout.Spacer
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.input.CheckboxInput
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.feature.account.setup.R
|
||||
import app.k9mail.feature.account.setup.domain.input.BooleanInputField
|
||||
import app.k9mail.feature.account.setup.ui.autoconfig.toResourceString
|
||||
|
||||
@Composable
|
||||
internal fun ConfigurationApprovalView(
|
||||
approvalState: BooleanInputField,
|
||||
onConfigurationApprovalChange: (Boolean) -> Unit,
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
|
||||
Spacer(modifier = Modifier.height(MainTheme.spacings.default))
|
||||
|
||||
CheckboxInput(
|
||||
|
@ -24,7 +28,7 @@ internal fun ConfigurationApprovalView(
|
|||
),
|
||||
checked = approvalState.value ?: false,
|
||||
onCheckedChange = onConfigurationApprovalChange,
|
||||
errorMessage = approvalState.error?.toString(), // TODO
|
||||
errorMessage = approvalState.error?.toResourceString(resources),
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
<string name="account_setup_auto_config_loading_message">Finding email details</string>
|
||||
<string name="account_setup_auto_config_loading_error">Failed to load email configuration</string>
|
||||
|
||||
<string name="account_setup_auto_config_status_header_title_configuration_found">Configuration Found</string>
|
||||
<string name="account_setup_auto_config_status_header_title_configuration_not_found">Configuration Not Found</string>
|
||||
<string name="account_setup_auto_config_status_header_subtitle_configuration_trusted">Configure automatically</string>
|
||||
|
@ -32,6 +31,7 @@
|
|||
<string name="account_setup_auto_config_status_header_subtitle_configuration_not_found">Configure manually</string>
|
||||
<string name="account_setup_auto_config_status_edit_configuration_button_label">Edit configuration</string>
|
||||
<string name="account_setup_auto_config_status_checkbox_configuration_untrusted_confirmation_label">I trust this configuration</string>
|
||||
<string name="account_setup_error_configuration_approval_required">It is required to approve the configuration.</string>
|
||||
|
||||
<string name="account_setup_incoming_config_top_bar_title">Incoming server settings</string>
|
||||
<string name="account_setup_incoming_config_protocol_type_label">Protocol</string>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package app.k9mail.feature.account.setup.domain.usecase
|
||||
|
||||
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.prop
|
||||
import org.junit.Test
|
||||
|
||||
class ValidateConfigurationApprovalTest {
|
||||
|
||||
@Test
|
||||
fun `should succeed when auto discovery is approved and trusted`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = true, isAutoDiscoveryTrusted = true)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Success::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should succeed when auto discovery not approved but is trusted`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = false, isAutoDiscoveryTrusted = true)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Success::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should succeed when auto discovery is approved but not trusted`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = true, isAutoDiscoveryTrusted = false)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Success::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when auto discovery is not approved and not trusted`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = false, isAutoDiscoveryTrusted = false)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Failure::class)
|
||||
.prop(ValidationResult.Failure::error)
|
||||
.isInstanceOf(ValidateConfigurationApproval.ValidateConfigurationApprovalError.ApprovalRequired::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should succeed when auto discovery isApproved null and is trusted`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = null, isAutoDiscoveryTrusted = true)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Success::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when auto discovery is isApproved null and is not trusted`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = null, isAutoDiscoveryTrusted = false)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Failure::class)
|
||||
.prop(ValidationResult.Failure::error)
|
||||
.isInstanceOf(ValidateConfigurationApproval.ValidateConfigurationApprovalError.ApprovalRequired::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when auto discovery is approved and trusted is null`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = false, isAutoDiscoveryTrusted = null)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Failure::class)
|
||||
.prop(ValidationResult.Failure::error)
|
||||
.isInstanceOf(ValidateConfigurationApproval.ValidateConfigurationApprovalError.ApprovalRequired::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when auto discovery is not approved and trusted is null`() {
|
||||
val useCase = ValidateConfigurationApproval()
|
||||
|
||||
val result = useCase.execute(isApproved = false, isAutoDiscoveryTrusted = null)
|
||||
|
||||
assertThat(result).isInstanceOf(ValidationResult.Failure::class)
|
||||
.prop(ValidationResult.Failure::error)
|
||||
.isInstanceOf(ValidateConfigurationApproval.ValidateConfigurationApprovalError.ApprovalRequired::class)
|
||||
}
|
||||
}
|
|
@ -288,6 +288,11 @@ class AccountAutoConfigViewModelTest {
|
|||
error = null,
|
||||
isValid = true,
|
||||
),
|
||||
configurationApproved = BooleanInputField(
|
||||
value = null,
|
||||
error = null,
|
||||
isValid = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -344,6 +349,11 @@ class AccountAutoConfigViewModelTest {
|
|||
error = TestError,
|
||||
isValid = false,
|
||||
),
|
||||
configurationApproved = BooleanInputField(
|
||||
value = null,
|
||||
error = null,
|
||||
isValid = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,12 @@ import app.k9mail.core.common.domain.usecase.validation.ValidationResult
|
|||
class FakeAccountAutoConfigValidator(
|
||||
private val emailAddressAnswer: ValidationResult = ValidationResult.Success,
|
||||
private val passwordAnswer: ValidationResult = ValidationResult.Success,
|
||||
private val configurationApprovalAnswer: ValidationResult = ValidationResult.Success,
|
||||
) : AccountAutoConfigContract.Validator {
|
||||
override fun validateEmailAddress(emailAddress: String): ValidationResult = emailAddressAnswer
|
||||
override fun validatePassword(password: String): ValidationResult = passwordAnswer
|
||||
override fun validateConfigurationApproval(
|
||||
isApproved: Boolean?,
|
||||
isAutoDiscoveryTrusted: Boolean?,
|
||||
): ValidationResult = configurationApprovalAnswer
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue