Skip to content

Commit

Permalink
Merge pull request #7174 from thundernest/add_account_updater
Browse files Browse the repository at this point in the history
Add account updater
  • Loading branch information
wmontwe authored Sep 15, 2023
2 parents 8f91165 + 8549bfc commit bfe82bf
Show file tree
Hide file tree
Showing 18 changed files with 165 additions and 18 deletions.
1 change: 1 addition & 0 deletions app-feature-preview/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ dependencies {

implementation(projects.feature.onboarding)
implementation(projects.feature.account.setup)
implementation(projects.feature.account.edit)

implementation(libs.appauth)
implementation(libs.okhttp)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package app.k9mail.feature.preview

import app.k9mail.core.common.oauth.OAuthConfigurationFactory
import app.k9mail.feature.account.edit.AccountEditExternalContract
import app.k9mail.feature.account.setup.AccountSetupExternalContract
import app.k9mail.feature.account.setup.featureAccountSetupModule
import app.k9mail.feature.preview.account.AccountCreator
import app.k9mail.feature.preview.account.AccountOwnerNameProvider
import app.k9mail.feature.preview.account.InMemoryAccountStore
import app.k9mail.feature.preview.auth.AndroidKeyStoreDirectoryProvider
import app.k9mail.feature.preview.auth.AppOAuthConfigurationFactory
import app.k9mail.feature.preview.auth.DefaultTrustedSocketFactory
Expand All @@ -15,11 +16,18 @@ import com.fsck.k9.mail.ssl.LocalKeyStore
import com.fsck.k9.mail.ssl.TrustManagerFactory
import com.fsck.k9.mail.ssl.TrustedSocketFactory
import org.koin.core.module.Module
import org.koin.dsl.binds
import org.koin.dsl.module

val accountModule: Module = module {
single { InMemoryAccountStore() }
.binds(
arrayOf(
AccountSetupExternalContract.AccountCreator::class,
AccountEditExternalContract.AccountUpdater::class,
),
)
factory<AccountSetupExternalContract.AccountOwnerNameProvider> { AccountOwnerNameProvider() }
factory<AccountSetupExternalContract.AccountCreator> { AccountCreator() }
}

val featureModule: Module = module {
Expand All @@ -31,5 +39,8 @@ val featureModule: Module = module {
single<TrustedSocketFactory> { DefaultTrustedSocketFactory(get(), get()) }
single<OAuth2TokenProviderFactory> { RealOAuth2TokenProviderFactory(context = get()) }

includes(featureAccountSetupModule, accountModule)
includes(
accountModule,
featureAccountSetupModule,
)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package app.k9mail.feature.preview.account

import app.k9mail.feature.account.common.domain.entity.Account
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdater
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdater.AccountUpdaterResult
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult

class InMemoryAccountStore(
private val accountMap: MutableMap<String, Account> = mutableMapOf(),
) : AccountCreator, AccountUpdater {

override suspend fun createAccount(account: Account): AccountCreatorResult {
accountMap[account.uuid] = account

return AccountCreatorResult.Success(account.uuid)
}

override suspend fun updateAccount(account: Account): AccountUpdaterResult {
return if (!accountMap.containsKey(account.uuid)) {
AccountUpdaterResult.Error("Account not found")
} else {
accountMap[account.uuid] = account

AccountUpdaterResult.Success(account.uuid)
}
}
}
5 changes: 4 additions & 1 deletion app/core/src/main/java/com/fsck/k9/Preferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ class Preferences internal constructor(
}
}

@OptIn(ExperimentalCoroutinesApi::class)
override fun getAccountFlow(accountUuid: String): Flow<Account> {
return callbackFlow {
val initialAccount = getAccount(accountUuid)
Expand Down Expand Up @@ -169,6 +168,10 @@ class Preferences internal constructor(

fun newAccount(): Account {
val accountUuid = UUID.randomUUID().toString()
return newAccount(accountUuid)
}

fun newAccount(accountUuid: String): Account {
val account = Account(accountUuid)
accountPreferenceSerializer.loadDefaults(account)

Expand Down
1 change: 1 addition & 0 deletions app/k9mail/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation(projects.feature.launcher)

implementation(projects.feature.account.setup)
implementation(projects.feature.account.edit)

implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class AccountCreator(
}

private fun create(account: Account): String {
val newAccount = preferences.newAccount()
val newAccount = preferences.newAccount(account.uuid)

newAccount.email = account.emailAddress

Expand Down
7 changes: 7 additions & 0 deletions app/k9mail/src/main/java/com/fsck/k9/account/AccountModule.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fsck.k9.account

import app.k9mail.feature.account.edit.AccountEditExternalContract
import app.k9mail.feature.account.setup.AccountSetupExternalContract
import org.koin.android.ext.koin.androidApplication
import org.koin.dsl.module
Expand All @@ -19,4 +20,10 @@ val newAccountModule = module {
context = androidApplication(),
)
}

factory<AccountEditExternalContract.AccountUpdater> {
AccountUpdater(
preferences = get(),
)
}
}
44 changes: 44 additions & 0 deletions app/k9mail/src/main/java/com/fsck/k9/account/AccountUpdater.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.fsck.k9.account

import app.k9mail.feature.account.common.domain.entity.Account
import app.k9mail.feature.account.edit.AccountEditExternalContract
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdater.AccountUpdaterResult
import com.fsck.k9.Preferences
import com.fsck.k9.logging.Timber
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class AccountUpdater(
private val preferences: Preferences,
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : AccountEditExternalContract.AccountUpdater {

@Suppress("TooGenericExceptionCaught")
override suspend fun updateAccount(account: Account): AccountUpdaterResult {
return try {
withContext(coroutineDispatcher) {
AccountUpdaterResult.Success(update(account))
}
} catch (e: Exception) {
Timber.e(e, "Error while updating account")

AccountUpdaterResult.Error(e.message ?: "Unknown update account error")
}
}

private fun update(account: Account): String {
val uuid = account.uuid
require(uuid.isNotEmpty()) { "Can't update account without uuid" }

val existingAccount = preferences.getAccount(uuid)
require(existingAccount != null) { "Can't update non-existing account" }

existingAccount.incomingServerSettings = account.incomingServerSettings
existingAccount.outgoingServerSettings = account.outgoingServerSettings

preferences.saveAccount(existingAccount)

return uuid
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package app.k9mail.feature.account.common.domain.entity
import com.fsck.k9.mail.ServerSettings

data class Account(
val uuid: String,
val emailAddress: String,
val incomingServerSettings: ServerSettings,
val outgoingServerSettings: ServerSettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package app.k9mail.feature.account.common.domain.entity
import com.fsck.k9.mail.ServerSettings

data class AccountState(
val uuid: String? = null,
val emailAddress: String? = null,
val incomingServerSettings: ServerSettings? = null,
val outgoingServerSettings: ServerSettings? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class InMemoryAccountStateRepositoryTest {

assertThat(result).isEqualTo(
AccountState(
uuid = null,
emailAddress = null,
incomingServerSettings = null,
outgoingServerSettings = null,
Expand All @@ -33,6 +34,7 @@ class InMemoryAccountStateRepositoryTest {
fun `should save state`() {
val testSubject = InMemoryAccountStateRepository(
AccountState(
uuid = "uuid",
emailAddress = "emailAddress",
incomingServerSettings = INCOMING_SERVER_SETTINGS,
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
Expand All @@ -41,6 +43,7 @@ class InMemoryAccountStateRepositoryTest {
),
)
val newState = AccountState(
uuid = "uuid2",
emailAddress = "emailAddress2",
incomingServerSettings = INCOMING_SERVER_SETTINGS.copy(host = "imap2.example.org"),
outgoingServerSettings = OUTGOING_SERVER_SETTINGS.copy(host = "smtp2.example.org"),
Expand Down Expand Up @@ -114,6 +117,7 @@ class InMemoryAccountStateRepositoryTest {
fun `should clear state`() {
val testSubject = InMemoryAccountStateRepository(
AccountState(
uuid = "uuid",
emailAddress = "emailAddress",
incomingServerSettings = INCOMING_SERVER_SETTINGS,
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
Expand All @@ -126,6 +130,7 @@ class InMemoryAccountStateRepositoryTest {

assertThat(testSubject.getState()).isEqualTo(
AccountState(
uuid = null,
emailAddress = null,
incomingServerSettings = null,
outgoingServerSettings = null,
Expand Down
28 changes: 28 additions & 0 deletions feature/account/edit/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
id(ThunderbirdPlugins.Library.androidCompose)
}

android {
namespace = "app.k9mail.feature.account.edit"
resourcePrefix = "account_edit_"

buildTypes {
debug {
manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
}
release {
manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
}
}
}

dependencies {
implementation(projects.core.ui.compose.designsystem)
implementation(projects.core.common)

implementation(projects.feature.account.common)
implementation(projects.feature.account.oauth)
implementation(projects.feature.account.server.validation)

testImplementation(projects.core.ui.compose.testing)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package app.k9mail.feature.account.edit

import app.k9mail.feature.account.common.domain.entity.Account

interface AccountEditExternalContract {

fun interface AccountUpdater {
suspend fun updateAccount(account: Account): AccountUpdaterResult

sealed interface AccountUpdaterResult {
data class Success(val accountId: String) : AccountUpdaterResult
data class Error(val message: String) : AccountUpdaterResult
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package app.k9mail.feature.account.edit

import app.k9mail.core.common.coreCommonModule
import app.k9mail.feature.account.oauth.featureAccountOAuthModule
import org.koin.dsl.module

val accountEditModule = module {
includes(coreCommonModule, featureAccountOAuthModule)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCrea
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
import com.fsck.k9.mail.ServerSettings
import java.util.UUID

class CreateAccount(
private val accountCreator: AccountCreator,
private val uuidGenerator: () -> String = { UUID.randomUUID().toString() },
) : UseCase.CreateAccount {
override suspend fun execute(
emailAddress: String,
Expand All @@ -18,6 +20,7 @@ class CreateAccount(
options: AccountOptions,
): String {
val account = Account(
uuid = uuidGenerator(),
emailAddress = emailAddress,
incomingServerSettings = incomingServerSettings,
outgoingServerSettings = outgoingServerSettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class CreateAccountTest {
recordedAccount = account
AccountCreatorResult.Success(accountUuid = "uuid")
},
uuidGenerator = { "uuid" },
)

val emailAddress = "[email protected]"
Expand Down Expand Up @@ -65,6 +66,7 @@ class CreateAccountTest {
assertThat(result).isEqualTo("uuid")
assertThat(recordedAccount).isEqualTo(
Account(
uuid = "uuid",
emailAddress = emailAddress,
incomingServerSettings = incomingServerSettings,
outgoingServerSettings = outgoingServerSettings,
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ include(

include(
":feature:account:common",
":feature:account:edit",
":feature:account:oauth",
":feature:account:setup",
":feature:account:server:certificate",
Expand Down

0 comments on commit bfe82bf

Please sign in to comment.