Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Use DataStore Approach - Part #1 (EXPOSUREAPP-13157) #5221

Merged
merged 27 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4a440a6
Added some DataStoreExtensions, DataStoreExtensionsTest
BMItr May 19, 2022
5eb21d7
Set FlowPreference deprecated
BMItr May 19, 2022
9e209b2
Added BaseJsonSerializer
BMItr May 23, 2022
2875ad8
Throw CorruptionException
BMItr May 23, 2022
ebd37cb
BugReportingSettings -> UploadHistoryStorage
BMItr May 23, 2022
c87a7ca
Deprecated
BMItr May 23, 2022
2f38c52
Added java time module for jackson
BMItr May 23, 2022
9b6a4e9
Merge branch 'release/2.23.x' into feature/12793-datastore-everywhere
BMItr May 23, 2022
dc9cc63
Restore with Gson
BMItr May 23, 2022
be48538
Added FakeTypedDataStore
BMItr May 23, 2022
3f9dab3
Made UploadHistoryStorage resettable
BMItr May 23, 2022
3f125a8
Adjusted tests
BMItr May 23, 2022
3a38d31
Added UploadHistoryStorageTest
BMItr May 23, 2022
91a7a90
Removed BugReportingSettings
BMItr May 23, 2022
1125b7b
Better logging
BMItr May 23, 2022
c4f81dc
Added UploadHistorySerializerTest
BMItr May 23, 2022
b0412b7
Added UploadHistoryStorageModuleTest
BMItr May 23, 2022
f652f12
better Code
BMItr May 23, 2022
272d1da
Merge branch 'release/2.23.x' into feature/12793-datastore-everywhere
BMItr May 23, 2022
5d4db50
clean
BMItr May 23, 2022
95dfc16
trigger CI
BMItr May 23, 2022
3a4f8d9
clean
BMItr May 23, 2022
8d77577
Merge branch 'release/2.23.x' into feature/12793-datastore-everywhere
BMItr May 24, 2022
fe56354
Comments
BMItr May 24, 2022
04e809d
clean
BMItr May 24, 2022
7132886
removed deprecated annotations
BMItr May 24, 2022
0861713
Merge branch 'release/2.24.x' into feature/12793-datastore-everywhere
mtwalli May 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Corona-Warn-App/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ dependencies {
implementation("org.boofcv:boofcv-android:$boofCV_version") {
exclude group: 'com.google.protobuf'
}

constraints {
implementation('net.lingala.zip4j:zip4j:2.10.0') {
because 'fixing zip4j vulnerability'
Expand Down Expand Up @@ -487,7 +488,11 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'com.google.guava:guava:30.0-android'
implementation "joda-time:joda-time:2.10.13"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-joda:2.13.2"

// Jackson
def jackson = "2.13.2"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson"

implementation 'com.networknt:json-schema-validator:1.0.69'
implementation 'net.swiftzer.semver:semver:1.2.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import de.rki.coronawarnapp.bugreporting.debuglog.DebugLogger
import de.rki.coronawarnapp.bugreporting.debuglog.internal.LogSnapshotter
import de.rki.coronawarnapp.bugreporting.debuglog.ui.DebugLogFragment
import de.rki.coronawarnapp.bugreporting.debuglog.ui.DebugLogViewModel
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage.UploadHistoryStorage
import de.rki.coronawarnapp.nearby.ENFClient
import io.mockk.MockKAnnotations
import io.mockk.every
Expand All @@ -29,7 +30,7 @@ class DebugLogFragmentTest : BaseUITest() {

@MockK lateinit var debugLogger: DebugLogger
@MockK lateinit var enfClient: ENFClient
@MockK lateinit var bugReportingSettings: BugReportingSettings
@MockK lateinit var uploadHistoryStorage: UploadHistoryStorage
@MockK lateinit var logSnapshotter: LogSnapshotter

private lateinit var inactiveViewModel: DebugLogViewModel
Expand Down Expand Up @@ -85,11 +86,11 @@ class DebugLogFragmentTest : BaseUITest() {
): DebugLogViewModel {
val vm = spyk(
DebugLogViewModel(
debugLogger,
TestDispatcherProvider(),
enfClient,
bugReportingSettings,
logSnapshotter
debugLogger = debugLogger,
dispatcherProvider = TestDispatcherProvider(),
enfClient = enfClient,
uploadHistoryStorage = uploadHistoryStorage,
logSnapshotter = logSnapshotter
)
)
with(vm) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import de.rki.coronawarnapp.bugreporting.censors.submission.RACoronaTestCensor
import de.rki.coronawarnapp.bugreporting.censors.submission.RapidQrCodeCensor
import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebugLoggerScope
import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage.UploadHistoryStorageModule
import de.rki.coronawarnapp.bugreporting.debuglog.upload.server.LogUploadApiV1
import de.rki.coronawarnapp.bugreporting.debuglog.upload.server.auth.LogUploadAuthApiV1
import de.rki.coronawarnapp.environment.bugreporting.LogUploadHttpClient
import de.rki.coronawarnapp.environment.bugreporting.LogUploadServerUrl
import de.rki.coronawarnapp.environment.datadonation.DataDonationCDNHttpClient
import de.rki.coronawarnapp.environment.datadonation.DataDonationCDNServerUrl
import de.rki.coronawarnapp.util.CWADebug
import de.rki.coronawarnapp.util.reset.Resettable
import kotlinx.coroutines.CoroutineScope
import okhttp3.OkHttpClient
import retrofit2.Retrofit
Expand All @@ -42,7 +42,12 @@ import retrofit2.converter.protobuf.ProtoConverterFactory
import javax.inject.Singleton

@Suppress("TooManyFunctions")
@Module(includes = [BugReportingSharedModule.BindsModule::class, BugReportingSharedModule.ResetModule::class])
@Module(
includes = [
BugReportingSharedModule.BindsModule::class,
UploadHistoryStorageModule::class
]
)
object BugReportingSharedModule {

@Reusable
Expand Down Expand Up @@ -84,14 +89,6 @@ object BugReportingSharedModule {
@Provides
fun scope(): CoroutineScope = DebugLoggerScope

@Module
internal interface ResetModule {

@Binds
@IntoSet
fun bindResettableBugReportingSettings(resettable: BugReportingSettings): Resettable
}

@Module
internal interface BindsModule {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.bugreporting.BugReportingSettings
import de.rki.coronawarnapp.bugreporting.debuglog.DebugLogger
import de.rki.coronawarnapp.bugreporting.debuglog.internal.LogSnapshotter
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage.UploadHistoryStorage
import de.rki.coronawarnapp.nearby.ENFClient
import de.rki.coronawarnapp.util.CWADebug
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
Expand All @@ -22,13 +22,13 @@ class DebugLogViewModel @AssistedInject constructor(
private val debugLogger: DebugLogger,
dispatcherProvider: DispatcherProvider,
private val enfClient: ENFClient,
bugReportingSettings: BugReportingSettings,
uploadHistoryStorage: UploadHistoryStorage,
private val logSnapshotter: LogSnapshotter
) : CWAViewModel(dispatcherProvider = dispatcherProvider) {

private val isActionInProgress = MutableStateFlow(false)

val logUploads = bugReportingSettings.uploadHistory.flow
val logUploads = uploadHistoryStorage.uploadHistory
.asLiveData(context = dispatcherProvider.Default)

val state: LiveData<State> = combine(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.bugreporting.BugReportingSettings
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.LogUpload
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage.UploadHistoryStorage
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
import kotlinx.coroutines.flow.map

class LogUploadHistoryViewModel @AssistedInject constructor(
dispatcherProvider: DispatcherProvider,
bugReportingSettings: BugReportingSettings
uploadHistoryStorage: UploadHistoryStorage
) : CWAViewModel(dispatcherProvider = dispatcherProvider) {

val logUploads: LiveData<List<LogUpload>> = bugReportingSettings.uploadHistory.flow
val logUploads: LiveData<List<LogUpload>> = uploadHistoryStorage.uploadHistory
.map { history -> history.logs.sortedByDescending { it.uploadedAt } }
.asLiveData(context = dispatcherProvider.Default)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package de.rki.coronawarnapp.bugreporting.debuglog.upload

import de.rki.coronawarnapp.bugreporting.BugReportingSettings
import de.rki.coronawarnapp.bugreporting.debuglog.internal.LogSnapshotter
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.LogUpload
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage.UploadHistoryStorage
import de.rki.coronawarnapp.bugreporting.debuglog.upload.server.LogUploadServer
import de.rki.coronawarnapp.bugreporting.debuglog.upload.server.auth.LogUploadAuthorizer
import timber.log.Timber
Expand All @@ -14,7 +14,7 @@ class SnapshotUploader @Inject constructor(
private val snapshotter: LogSnapshotter,
private val uploadServer: LogUploadServer,
private val authorizer: LogUploadAuthorizer,
private val bugReportingSettings: BugReportingSettings
private val uploadHistoryStorage: UploadHistoryStorage
) {

suspend fun uploadSnapshot(): LogUpload {
Expand All @@ -38,7 +38,7 @@ class SnapshotUploader @Inject constructor(
}
}

bugReportingSettings.uploadHistory.update { oldHistory ->
uploadHistoryStorage.update { oldHistory ->
val newLogs = oldHistory.logs.toMutableList()
if (newLogs.size >= 10) {
newLogs.removeFirst().also {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package de.rki.coronawarnapp.bugreporting.debuglog.upload.history

import com.fasterxml.jackson.annotation.JsonProperty
import java.time.Instant

data class LogUpload(
BMItr marked this conversation as resolved.
Show resolved Hide resolved
val id: String,
val uploadedAt: Instant
@JsonProperty("id") val id: String,
@JsonProperty("uploadedAt") val uploadedAt: Instant
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.rki.coronawarnapp.bugreporting.debuglog.upload.history

import com.fasterxml.jackson.annotation.JsonProperty

data class UploadHistory(
val logs: List<LogUpload> = emptyList()
@JsonProperty("logs") val logs: List<LogUpload> = emptyList()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage

import com.fasterxml.jackson.databind.ObjectMapper
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.UploadHistory
import de.rki.coronawarnapp.util.datastore.BaseJsonSerializer
import de.rki.coronawarnapp.util.serialization.BaseJackson
import javax.inject.Inject

class UploadHistorySerializer @Inject constructor(
@BaseJackson objectMapper: ObjectMapper
) : BaseJsonSerializer<UploadHistory>(objectMapper) {

override val defaultValue: UploadHistory
get() = UploadHistory()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage

import androidx.datastore.core.DataStore
import dagger.Reusable
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.UploadHistory
import de.rki.coronawarnapp.tag
import de.rki.coronawarnapp.util.reset.Resettable
import kotlinx.coroutines.flow.catch
import timber.log.Timber
import javax.inject.Inject

@Reusable
class UploadHistoryStorage @Inject constructor(
private val dataStore: DataStore<UploadHistory>
) : Resettable {

val uploadHistory = dataStore.data.catch {
Timber.tag(TAG).e("Failed to get UploadHistory")
emit(UploadHistory())
}

suspend fun update(transform: suspend (t: UploadHistory) -> UploadHistory) {
dataStore.updateData(transform)
}

override suspend fun reset() {
Timber.tag(TAG).d("reset")
dataStore.updateData { UploadHistory() }
}
}

private val TAG = tag<UploadHistoryStorage>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package de.rki.coronawarnapp.bugreporting.debuglog.upload.history.storage

import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import androidx.datastore.migrations.SharedPreferencesMigration
import com.google.gson.Gson
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
import de.rki.coronawarnapp.bugreporting.debuglog.upload.history.UploadHistory
import de.rki.coronawarnapp.util.coroutine.AppScope
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.di.AppContext
import de.rki.coronawarnapp.util.reset.Resettable
import de.rki.coronawarnapp.util.serialization.BaseGson
import de.rki.coronawarnapp.util.serialization.fromJson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.plus
import timber.log.Timber
import javax.inject.Singleton

@Module(includes = [UploadHistoryStorageModule.ResetModule::class])
object UploadHistoryStorageModule {

@Singleton
@Provides
fun provideDataStore(
serializer: UploadHistorySerializer,
@AppScope appScope: CoroutineScope,
dispatcherProvider: DispatcherProvider,
@AppContext context: Context,
migration: SharedPreferencesMigration<UploadHistory>
): DataStore<UploadHistory> = DataStoreFactory.create(
serializer = serializer,
scope = appScope + dispatcherProvider.IO,
migrations = listOf(migration)
) {
context.dataStoreFile(UPLOAD_HISTORY_DATA_STORE)
}

@Provides
fun provideMigration(
@AppContext context: Context,
@BaseGson gson: Gson
) = SharedPreferencesMigration<UploadHistory>(
context = context,
sharedPreferencesName = LEGACY_SHARED_PREFS
) { sharedPreferencesView, uploadHistory ->
val migratedUploadHistory = runCatching {
// Data was converted with Gson before so use Gson to restore data to avoid corrupted data
// Gson and Jackson store Instants differently
sharedPreferencesView.getString(LEGACY_UPLOAD_HISTORY_KEY)?.let { gson.fromJson<UploadHistory>(it) }
}
.onFailure { Timber.tag("SharedPreferencesMigration<UploadHistory>").e(it, "Migration failed") }
.getOrNull()

migratedUploadHistory ?: uploadHistory
}

@Module
internal interface ResetModule {

@Binds
@IntoSet
fun bindResettableUploadHistoryStorage(resettable: UploadHistoryStorage): Resettable
}
}

private const val UPLOAD_HISTORY_DATA_STORE: String = "upload_history_data_store"
private const val LEGACY_SHARED_PREFS = "bugreporting_localdata"
@VisibleForTesting const val LEGACY_UPLOAD_HISTORY_KEY = "upload.history"
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package de.rki.coronawarnapp.util.datastore

import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import com.fasterxml.jackson.databind.ObjectMapper
import timber.log.Timber
import java.io.InputStream
import java.io.OutputStream

abstract class BaseJsonSerializer<T : Any>(
private val objectMapper: ObjectMapper
) : Serializer<T> {

private val type get() = defaultValue::class.java

override suspend fun readFrom(input: InputStream): T = runCatching { objectMapper.readValue(input, type) }
.onFailure { throw CorruptionException("Failed to read data of type=$type", it) }
.getOrThrow()

override suspend fun writeTo(t: T, output: OutputStream) {
runCatching { objectMapper.writeValue(output, t) }
.onFailure { Timber.tag(TAG).w(it, "Failed to write data=$t") }
}
}

private const val TAG = "BaseJsonSerializer"
Loading