Create API token when updating from 24

This commit is contained in:
Kirill Kamakin
2022-12-11 17:45:22 +01:00
parent dd313def96
commit 3720b1aadb
12 changed files with 170 additions and 17 deletions

View File

@@ -4,7 +4,12 @@ import android.app.Application
import com.google.android.material.color.DynamicColors
import dagger.hilt.android.HiltAndroidApp
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
import gq.kirmanak.mealient.data.migration.MigrationDetector
import gq.kirmanak.mealient.logging.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltAndroidApp
@@ -16,9 +21,15 @@ class App : Application() {
@Inject
lateinit var buildConfiguration: BuildConfiguration
@Inject
lateinit var migrationDetector: MigrationDetector
private val appCoroutineScope = CoroutineScope(Dispatchers.Main + Job())
override fun onCreate() {
super.onCreate()
logger.v { "onCreate() called" }
DynamicColors.applyToActivitiesIfAvailable(this)
appCoroutineScope.launch { migrationDetector.executeMigrations() }
}
}

View File

@@ -0,0 +1,19 @@
package gq.kirmanak.mealient.data.configuration
import gq.kirmanak.mealient.BuildConfig
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BuildConfigurationImpl @Inject constructor() : BuildConfiguration {
@get:JvmName("_isDebug")
private val isDebug by lazy { BuildConfig.DEBUG }
private val versionCode by lazy { BuildConfig.VERSION_CODE }
override fun isDebug(): Boolean = isDebug
override fun versionCode(): Int = versionCode
}

View File

@@ -0,0 +1,41 @@
package gq.kirmanak.mealient.data.migration
import android.content.SharedPreferences
import androidx.core.content.edit
import gq.kirmanak.mealient.data.auth.AuthRepo
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
import gq.kirmanak.mealient.datastore.DataStoreModule.Companion.ENCRYPTED
import gq.kirmanak.mealient.logging.Logger
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
@Singleton
class From24AuthMigrationExecutor @Inject constructor(
@Named(ENCRYPTED) private val sharedPreferences: SharedPreferences,
private val authRepo: AuthRepo,
private val logger: Logger,
) : MigrationExecutor {
override val migratingFrom: Int = 24
override suspend fun executeMigration() {
logger.v { "executeMigration() was called" }
val email = sharedPreferences.getString(EMAIL_KEY, null)
val password = sharedPreferences.getString(PASSWORD_KEY, null)
if (email != null && password != null) {
runCatchingExceptCancel { authRepo.authenticate(email, password) }
.onFailure { logger.e(it) { "API token creation failed" } }
.onSuccess { logger.i { "Created API token during migration" } }
}
sharedPreferences.edit {
remove(EMAIL_KEY)
remove(PASSWORD_KEY)
}
}
companion object {
private const val EMAIL_KEY = "email"
private const val PASSWORD_KEY = "password"
}
}

View File

@@ -0,0 +1,6 @@
package gq.kirmanak.mealient.data.migration
interface MigrationDetector {
suspend fun executeMigrations()
}

View File

@@ -0,0 +1,43 @@
package gq.kirmanak.mealient.data.migration
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
import gq.kirmanak.mealient.data.storage.PreferencesStorage
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
import gq.kirmanak.mealient.logging.Logger
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class MigrationDetectorImpl @Inject constructor(
private val preferencesStorage: PreferencesStorage,
private val migrationExecutors: Set<@JvmSuppressWildcards MigrationExecutor>,
private val buildConfiguration: BuildConfiguration,
private val logger: Logger,
) : MigrationDetector {
override suspend fun executeMigrations() {
val key = preferencesStorage.lastExecutedMigrationVersionKey
val lastVersion = preferencesStorage.getValue(key) ?: VERSION_BEFORE_MIGRATION_IMPLEMENTED
val currentVersion = buildConfiguration.versionCode()
logger.i { "Last migration version is $lastVersion, current is $currentVersion" }
if (lastVersion != currentVersion) {
migrationExecutors
.filter { it.migratingFrom <= lastVersion }
.forEach {
runCatchingExceptCancel { it.executeMigration() }
.onFailure { logger.e(it) { "Migration executor failed: $it" } }
.onSuccess { logger.i { "Migration executor succeeded: $it" } }
}
}
preferencesStorage.storeValues(Pair(key, currentVersion))
}
companion object {
private const val VERSION_BEFORE_MIGRATION_IMPLEMENTED = 24
}
}

View File

@@ -0,0 +1,8 @@
package gq.kirmanak.mealient.data.migration
interface MigrationExecutor {
val migratingFrom: Int
suspend fun executeMigration()
}

View File

@@ -11,6 +11,8 @@ interface PreferencesStorage {
val isDisclaimerAcceptedKey: Preferences.Key<Boolean>
val lastExecutedMigrationVersionKey: Preferences.Key<Int>
suspend fun <T> getValue(key: Preferences.Key<T>): T?
suspend fun <T> requireValue(key: Preferences.Key<T>): T

View File

@@ -4,9 +4,15 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import gq.kirmanak.mealient.logging.Logger
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
import javax.inject.Singleton
@@ -22,6 +28,9 @@ class PreferencesStorageImpl @Inject constructor(
override val isDisclaimerAcceptedKey = booleanPreferencesKey("isDisclaimedAccepted")
override val lastExecutedMigrationVersionKey: Preferences.Key<Int> =
intPreferencesKey("lastExecutedMigrationVersion")
override suspend fun <T> getValue(key: Preferences.Key<T>): T? {
val value = dataStore.data.first()[key]
logger.v { "getValue() returned: $value for $key" }

View File

@@ -1,11 +1,11 @@
package gq.kirmanak.mealient.architecture
package gq.kirmanak.mealient.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
import gq.kirmanak.mealient.architecture.configuration.BuildConfigurationImpl
import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl
import javax.inject.Singleton
@Module

View File

@@ -0,0 +1,26 @@
package gq.kirmanak.mealient.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoSet
import gq.kirmanak.mealient.data.migration.From24AuthMigrationExecutor
import gq.kirmanak.mealient.data.migration.MigrationDetector
import gq.kirmanak.mealient.data.migration.MigrationDetectorImpl
import gq.kirmanak.mealient.data.migration.MigrationExecutor
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
interface MigrationModule {
@Binds
@Singleton
@IntoSet
fun bindFrom24AuthMigrationExecutor(from24AuthMigrationExecutor: From24AuthMigrationExecutor): MigrationExecutor
@Binds
@Singleton
fun bindMigrationDetector(migrationDetectorImpl: MigrationDetectorImpl): MigrationDetector
}

View File

@@ -3,4 +3,6 @@ package gq.kirmanak.mealient.architecture.configuration
interface BuildConfiguration {
fun isDebug(): Boolean
fun versionCode(): Int
}

View File

@@ -1,14 +0,0 @@
package gq.kirmanak.mealient.architecture.configuration
import gq.kirmanak.mealient.architecture.BuildConfig
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BuildConfigurationImpl @Inject constructor() : BuildConfiguration {
@get:JvmName("_isDebug")
private val isDebug by lazy { BuildConfig.DEBUG }
override fun isDebug(): Boolean = isDebug
}