diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt index 1c1441fb24dba0532ac10ff9c8f31bb9695f4b4e..b0e02bfce947e3a0776b4ed9eca95af2ca08d524 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt @@ -71,7 +71,6 @@ class DebugOptionsFragment : Fragment(R.layout.fragment_test_debugoptions), Auto environmentCdnurlVerification.text = "Verification CDN:\n${state.urlVerification}" environmentUrlDatadonation.text = "DataDonation:\n${state.urlDataDonation}" environmentUrlLogUpload.text = "LogUpload:\n${state.urlLogUpload}" - environmentCdnUrlVaccination.text = "Vaccination CDN: \n${state.urlVaccination}" environmentPubkeyCrowdnotifier.text = "CrowdNotifierPubKey:\n${state.pubKeyCrowdNotifier}" environmentPubkeyAppconfig.text = "AppConfigPubKey:\n${state.pubKeyAppConfig}" } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt index 604996995a54bc7ef8e0e241e677d3588afa17ce..c9b907793ab6812a252f519c71a9bee66af2387a 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt @@ -10,7 +10,6 @@ data class EnvironmentState( val urlVerification: String, val urlDataDonation: String, val urlLogUpload: String, - val urlVaccination: String, val pubKeyCrowdNotifier: String, val pubKeyAppConfig: String, ) { @@ -23,7 +22,6 @@ data class EnvironmentState( urlVerification = verificationCdnUrl, urlDataDonation = dataDonationCdnUrl, urlLogUpload = logUploadServerUrl, - urlVaccination = vaccinationCdnUrl, pubKeyCrowdNotifier = crowdNotifierPublicKey, pubKeyAppConfig = appConfigPublicKey, ) diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml index 60061a05c16fe75d0af8856d12ffec4d6388853b..165e76a5873ac28d6015f1d63ec99802ff2c4a4f 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml @@ -109,14 +109,6 @@ android:layout_marginTop="4dp" tools:text="LogUpload: ?" /> - <TextView - android:id="@+id/environment_cdn_url_vaccination" - style="@style/TextAppearance.MaterialComponents.Caption" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="4dp" - tools:text="Vaccination: ?" /> - <TextView android:id="@+id/environment_pubkey_appconfig" style="@style/TextAppearance.MaterialComponents.Caption" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt index 3a81cb926a78ecec4b9f6e8333b31fb15e075770..b99710e6d4a6bd5a07a5bb7d08931aecf4594746 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt @@ -5,7 +5,6 @@ import de.rki.coronawarnapp.environment.bugreporting.BugReportingServerModule import de.rki.coronawarnapp.environment.datadonation.DataDonationCDNModule import de.rki.coronawarnapp.environment.download.DownloadCDNModule import de.rki.coronawarnapp.environment.submission.SubmissionCDNModule -import de.rki.coronawarnapp.environment.vaccination.VaccinationCertificateUrlModule import de.rki.coronawarnapp.environment.verification.VerificationCDNModule @Module( @@ -14,8 +13,7 @@ import de.rki.coronawarnapp.environment.verification.VerificationCDNModule SubmissionCDNModule::class, VerificationCDNModule::class, DataDonationCDNModule::class, - BugReportingServerModule::class, - VaccinationCertificateUrlModule::class + BugReportingServerModule::class ] ) class EnvironmentModule diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt index f7d1a754da9017d98a45bc58ac599d469a978082..d44e8e88ef378eaae6e78f7c4e73591be7c08e93 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt @@ -12,7 +12,6 @@ import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.LOG_UPLOAD import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.SAFETYNET_API_KEY import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.SUBMISSION import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.USE_EUR_KEY_PKGS -import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.VACCINATION_VALUE import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.VERIFICATION import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.VERIFICATION_KEYS import de.rki.coronawarnapp.environment.EnvironmentSetup.Type.Companion.toEnvironmentType @@ -37,7 +36,6 @@ class EnvironmentSetup @Inject constructor( VERIFICATION_KEYS("PUB_KEYS_SIGNATURE_VERIFICATION"), DATA_DONATION("DATA_DONATION_CDN_URL"), LOG_UPLOAD("LOG_UPLOAD_SERVER_URL"), - VACCINATION_VALUE("VACCINATION_CDN_URL"), SAFETYNET_API_KEY("SAFETYNET_API_KEY"), CROWD_NOTIFIER_PUBLIC_KEY("CROWD_NOTIFIER_PUBLIC_KEY") } @@ -137,9 +135,6 @@ class EnvironmentSetup @Inject constructor( val logUploadServerUrl: String get() = getEnvironmentValue(LOG_UPLOAD).asString - val vaccinationCdnUrl: String - get() = getEnvironmentValue(VACCINATION_VALUE).asString - companion object { private const val PKEY_CURRENT_ENVINROMENT = "environment.current" } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/vaccination/VaccinationCertificateCDNUrl.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/vaccination/VaccinationCertificateCDNUrl.kt deleted file mode 100644 index 5740f4bf0ca8e587642020e693a8754b43e91b09..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/vaccination/VaccinationCertificateCDNUrl.kt +++ /dev/null @@ -1,8 +0,0 @@ -package de.rki.coronawarnapp.environment.vaccination - -import javax.inject.Qualifier - -@Qualifier -@MustBeDocumented -@Retention(AnnotationRetention.RUNTIME) -annotation class VaccinationCertificateCDNUrl diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/vaccination/VaccinationCertificateUrlModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/vaccination/VaccinationCertificateUrlModule.kt deleted file mode 100644 index 5660c7604074271c317ccd556e911bf78de18bb6..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/vaccination/VaccinationCertificateUrlModule.kt +++ /dev/null @@ -1,17 +0,0 @@ -package de.rki.coronawarnapp.environment.vaccination - -import dagger.Module -import dagger.Provides -import dagger.Reusable -import de.rki.coronawarnapp.environment.BaseEnvironmentModule -import de.rki.coronawarnapp.environment.EnvironmentSetup - -@Module -class VaccinationCertificateUrlModule : BaseEnvironmentModule() { - - @Reusable - @VaccinationCertificateCDNUrl - @Provides - fun provideVaccinationValueSetUrl(environmentSetup: EnvironmentSetup): String = - requireValidUrl(environmentSetup.vaccinationCdnUrl) -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt index ca5f244a11c0785cd63b06bede6576f00c49579a..27de6bffab3d6d14dd588f322d370025b3efdb51 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt @@ -1,18 +1,16 @@ package de.rki.coronawarnapp.vaccination.core.repository +import androidx.annotation.VisibleForTesting import dagger.Reusable import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage -import de.rki.coronawarnapp.vaccination.core.server.valueset.DefaultVaccinationValueSet import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationServer import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet +import de.rki.coronawarnapp.vaccination.core.server.valueset.isEmpty import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch import timber.log.Timber import java.util.Locale import javax.inject.Inject @@ -21,57 +19,55 @@ import javax.inject.Inject class ValueSetsRepository @Inject constructor( private val vaccinationServer: VaccinationServer, private val valueSetsStorage: ValueSetsStorage, - @AppScope appScope: CoroutineScope + @AppScope private val scope: CoroutineScope ) { - private val internalFlow = MutableStateFlow<Locale?>(null) + val latestValueSet: Flow<VaccinationValueSet> = valueSetsStorage.valueSet.flow.distinctUntilChanged() - val latestValueSet: Flow<VaccinationValueSet?> = internalFlow - .filterNotNull() - .map { - fromServer(it) ?: fromLocalStorage() ?: fromServer(Locale.ENGLISH) ?: createEmptyValueSet(it) - // Return empty value set as last resort - } - .stateIn( - scope = appScope, - started = SharingStarted.Lazily, - initialValue = createEmptyValueSet(Locale.ENGLISH) - ) - - fun reloadValueSet(languageCode: Locale) { - Timber.d("reloadValueSet(languageCode=%s)", languageCode) - internalFlow.value = languageCode - } + fun triggerUpdateValueSet(languageCode: Locale) = scope.launch { + Timber.d("triggerUpdateValueSet(languageCode=%s)", languageCode) + var valueSet = vaccinationServer.getVaccinationValueSets(languageCode = languageCode) - private suspend fun fromServer(languageCode: Locale): VaccinationValueSet? = try { - Timber.d("fromServer(languageCode=%s)", languageCode) - vaccinationServer.getVaccinationValueSets(languageCode = languageCode)?.also { - Timber.d("Saving new value sets %s", it) - valueSetsStorage.vaccinationValueSet = it + if (valueSet == null && valueSetsStorage.valueSet.value.isEmpty()) { + Timber.d( + "Got no value set from server for %s and local value set is empty... " + + "Try fallback to value set for en", + languageCode.language + ) + valueSet = vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } - } catch (e: Exception) { - Timber.w(e, "fromServer(): %s", e.message) - null - } - private fun fromLocalStorage(): VaccinationValueSet? = try { - Timber.d("fromLocalStorage()") - valueSetsStorage.vaccinationValueSet - } catch (e: Exception) { - Timber.w(e, "fromLocalStorage(): %s", e.message) - null + valueSet + .also { Timber.d("Value set is %s", it) } + ?.let { newValueSet -> valueSetsStorage.valueSet.update { newValueSet.toStoredVaccinationValueSet() } } } - private fun createEmptyValueSet(languageCode: Locale) = DefaultVaccinationValueSet( - languageCode = languageCode, - vp = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()), - mp = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()), - ma = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()) - ) - fun clear() { Timber.d("Clearing value sets") vaccinationServer.clear() valueSetsStorage.clear() } } + +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +fun VaccinationValueSet.toStoredVaccinationValueSet(): ValueSetsStorage.StoredVaccinationValueSet = + ValueSetsStorage.StoredVaccinationValueSet( + languageCode = languageCode, + vp = vp.toStoredValueSet(), + mp = mp.toStoredValueSet(), + ma = ma.toStoredValueSet() + ) + +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +fun VaccinationValueSet.ValueSet.toStoredValueSet(): ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet = + ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet( + items = items.map { it.toStoredItem() } + ) + +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +fun VaccinationValueSet.ValueSet.Item.toStoredItem(): + ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem = + ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem( + key = key, + displayText = displayText + ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt index e9d91b475b72a8d521cbf6e649407bc7f7c08f4d..bb81b8a8261a55e8bfa6cdcba1ed2f20ecb86064 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt @@ -3,12 +3,13 @@ package de.rki.coronawarnapp.vaccination.core.repository.storage import android.content.Context import android.content.SharedPreferences import androidx.annotation.Keep -import androidx.core.content.edit import com.google.gson.Gson import com.google.gson.annotations.SerializedName import dagger.Reusable import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.preferences.FlowPreference import de.rki.coronawarnapp.util.preferences.clearAndNotify +import de.rki.coronawarnapp.util.preferences.createFlowPreference import de.rki.coronawarnapp.util.serialization.BaseGson import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet import timber.log.Timber @@ -25,36 +26,26 @@ class ValueSetsStorage @Inject constructor( context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) } - var vaccinationValueSet: VaccinationValueSet? - get() = getValueSet() - set(value) = setValueSet(value) - - private fun getValueSet(): VaccinationValueSet? { - Timber.d("Loading value set") - return prefs.getString(PKEY_VALUE_SETS_PREFIX, null)?.let { - gson.fromJson(it, StoredVaccinationValueSet::class.java).also { loaded -> Timber.d("Loaded %s", loaded) } - }.also { Timber.d("Returning %s", it) } - } - - private fun setValueSet(value: VaccinationValueSet?) { - Timber.d("Saving %s", value) - value?.let { - prefs.edit { - val storeValue = it.toStoredVaccinationValueSet() - val json = gson.toJson(storeValue, StoredVaccinationValueSet::class.java) - Timber.d("String %s", json) - putString(PKEY_VALUE_SETS_PREFIX, json) - } - } - } + var valueSet: FlowPreference<StoredVaccinationValueSet> = prefs.createFlowPreference( + key = PKEY_VALUE_SETS_PREFIX, + reader = FlowPreference.gsonReader(gson = gson, createEmptyValueSet()), + writer = FlowPreference.gsonWriter(gson = gson) + ) fun clear() { Timber.d("Clearing local storage") prefs.clearAndNotify() } + private fun createEmptyValueSet() = StoredVaccinationValueSet( + languageCode = Locale.ENGLISH, + vp = StoredVaccinationValueSet.StoredValueSet(items = emptyList()), + mp = StoredVaccinationValueSet.StoredValueSet(items = emptyList()), + ma = StoredVaccinationValueSet.StoredValueSet(items = emptyList()) + ) + @Keep - private data class StoredVaccinationValueSet( + data class StoredVaccinationValueSet( @SerializedName("languageCode") override val languageCode: Locale, @SerializedName("vp") override val vp: StoredValueSet, @SerializedName("mp") override val mp: StoredValueSet, @@ -73,26 +64,7 @@ class ValueSetsStorage @Inject constructor( ) : VaccinationValueSet.ValueSet.Item } } - - private fun VaccinationValueSet.toStoredVaccinationValueSet(): StoredVaccinationValueSet = - StoredVaccinationValueSet( - languageCode = languageCode, - vp = vp.toStoredValueSet(), - mp = mp.toStoredValueSet(), - ma = ma.toStoredValueSet() - ) - - private fun VaccinationValueSet.ValueSet.toStoredValueSet(): StoredVaccinationValueSet.StoredValueSet = - StoredVaccinationValueSet.StoredValueSet( - items = items.map { it.toStoredItem() } - ) - - private fun VaccinationValueSet.ValueSet.Item.toStoredItem(): StoredVaccinationValueSet.StoredValueSet.StoredItem = - StoredVaccinationValueSet.StoredValueSet.StoredItem( - key = key, - displayText = displayText - ) } -private const val PREF_NAME = "valuesets_local" -private const val PKEY_VALUE_SETS_PREFIX = "valuesets" +private const val PREF_NAME = "valuesets_localdata" +private const val PKEY_VALUE_SETS_PREFIX = "valueset" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt index 2ebc6e08a1f3ce0f9de7d1f0deeaa12ff9acebf2..08618fc008c487cd471b334a15fe6df1d039a348 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt @@ -1,9 +1,7 @@ package de.rki.coronawarnapp.vaccination.core.server.valueset -import androidx.annotation.Keep import java.util.Locale -@Keep interface VaccinationValueSet { val languageCode: Locale val vp: ValueSet @@ -25,3 +23,7 @@ fun VaccinationValueSet.getDisplayText(key: String): String? = vp.getDisplayText(key) ?: mp.getDisplayText(key) ?: ma.getDisplayText(key) fun VaccinationValueSet.ValueSet.getDisplayText(key: String): String? = items.find { key == it.key }?.displayText + +fun VaccinationValueSet.isEmpty(): Boolean = vp.isEmpty() && mp.isEmpty() && ma.isEmpty() + +fun VaccinationValueSet.ValueSet.isEmpty(): Boolean = items.isEmpty() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetModule.kt index 0fd80cda3bf80f09da0109692a134f4995a9ff1f..2b3413d14169f086392e7ab3df76a867f2acb57a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetModule.kt @@ -25,7 +25,8 @@ class VaccinationValueSetModule { fun cache( @AppContext context: Context ): Cache { - val cacheDir = File(context.cacheDir, "vaccination_value") + val vaccDir = File(context.cacheDir, "vaccination") + val cacheDir = File(vaccDir, "valueset_httpcache") return Cache(cacheDir, CACHE_SIZE_5MB) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt index 1a9e0d6ee0e6139ee189533c67dd3e080902c488..a4e200153ccea27b40c5e3422cd31964e594ff8f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt @@ -41,7 +41,7 @@ class VaccinationListViewModel @AssistedInject constructor( ) : CWAViewModel() { init { - valueSetsRepository.reloadValueSet(languageCode = context.getLocale()) + valueSetsRepository.triggerUpdateValueSet(languageCode = context.getLocale()) } val events = SingleLiveEvent<Event>() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt index 869893adae7cea43d461d87dd4b3ceb7001221db..12b178e9db9fd377c5eadcb406fb522a264d50e9 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt @@ -68,7 +68,6 @@ class EnvironmentSetupTest : BaseTest() { dataDonationCdnUrl shouldBe "https://datadonation-${env.rawKey}" logUploadServerUrl shouldBe "https://logupload-${env.rawKey}" crowdNotifierPublicKey shouldBe "123_abc-${env.rawKey}" - vaccinationCdnUrl shouldBe "https://vaccination-${env.rawKey}" } } } @@ -128,8 +127,7 @@ class EnvironmentSetupTest : BaseTest() { EnvironmentSetup.EnvKey.LOG_UPLOAD.rawKey shouldBe "LOG_UPLOAD_SERVER_URL" EnvironmentSetup.EnvKey.SAFETYNET_API_KEY.rawKey shouldBe "SAFETYNET_API_KEY" EnvironmentSetup.EnvKey.CROWD_NOTIFIER_PUBLIC_KEY.rawKey shouldBe "CROWD_NOTIFIER_PUBLIC_KEY" - EnvironmentSetup.EnvKey.VACCINATION_VALUE.rawKey shouldBe "VACCINATION_CDN_URL" - EnvironmentSetup.EnvKey.values().size shouldBe 10 + EnvironmentSetup.EnvKey.values().size shouldBe 9 } companion object { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt index 5577560dc72f94ae8b79716c375227c270df1e73..3dbba9d531d6e633a583ac0c0e946e78a18fbb80 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt @@ -131,6 +131,7 @@ internal class DataResetTest : BaseTest() { ratProfileSettings.deleteProfile() vaccinationRepository.clear() vaccinationPreferences.clear() + valueSetsRepository.clear() } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt index 3edc082661868d7de4748dcb8356778d988835a2..374831b938ca7e4384a0ad152b7c144bdf7b1766 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt @@ -1,23 +1,26 @@ package de.rki.coronawarnapp.vaccination.core.repository +import de.rki.coronawarnapp.util.preferences.FlowPreference import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage import de.rki.coronawarnapp.vaccination.core.server.valueset.DefaultVaccinationValueSet import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationServer +import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations +import io.mockk.Ordering import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.runs -import io.mockk.verify import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest +import testhelpers.preferences.mockFlowPreference import java.util.Locale class ValueSetsRepositoryTest : BaseTest() { @@ -28,7 +31,8 @@ class ValueSetsRepositoryTest : BaseTest() { private val testScope = TestCoroutineScope() private val emptyValueSetEN = createValueSet(languageCode = Locale.ENGLISH) - private val emptyValueSetDE = createValueSet(languageCode = Locale.GERMAN) + private val valueSetPref: FlowPreference<ValueSetsStorage.StoredVaccinationValueSet> = + mockFlowPreference(emptyValueSetEN.toStoredVaccinationValueSet()) private val valueSetEN = createValueSet( languageCode = Locale.ENGLISH, @@ -89,125 +93,98 @@ class ValueSetsRepositoryTest : BaseTest() { private fun createInstance() = ValueSetsRepository( vaccinationServer = vaccinationServer, valueSetsStorage = valueSetsStorage, - appScope = testScope + scope = testScope ) @BeforeEach fun setUp() { MockKAnnotations.init(this) coEvery { vaccinationServer.getVaccinationValueSets(any()) } returns null + coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns valueSetEN + coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns valueSetDE every { vaccinationServer.clear() } just runs - every { valueSetsStorage.vaccinationValueSet } returns null - every { valueSetsStorage.vaccinationValueSet = any() } just runs + + every { valueSetsStorage.valueSet } returns valueSetPref every { valueSetsStorage.clear() } just runs } @Test - fun `default value is an empty value set EN`() = runBlockingTest { + fun `successful update for de`() = runBlockingTest { createInstance().run { - latestValueSet.first() shouldBe emptyValueSetEN + triggerUpdateValueSet(languageCode = Locale.GERMAN) + latestValueSet.first() + }.also { it.validateValues(valueSetDE) } - coVerify(exactly = 0) { - vaccinationServer.getVaccinationValueSets(any()) - valueSetsStorage.vaccinationValueSet - } + coVerify(exactly = 1) { + vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } - } - @Test - fun `falls back to empty value set with specified language code`() = runBlockingTest { - createInstance().run { - reloadValueSet(languageCode = Locale.GERMAN) - latestValueSet.first() shouldBe emptyValueSetDE - - coVerify(exactly = 1) { - valueSetsStorage.vaccinationValueSet - vaccinationServer.getVaccinationValueSets(Locale.GERMAN) - vaccinationServer.getVaccinationValueSets(Locale.ENGLISH) - } + coVerify(exactly = 0) { + vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } } @Test - fun `returns value set for specified language from server`() = runBlockingTest { - coEvery { vaccinationServer.getVaccinationValueSets(Locale.GERMAN) } returns valueSetDE - coEvery { vaccinationServer.getVaccinationValueSets(Locale.ENGLISH) } returns valueSetEN - + fun `fallback to en`() = runBlockingTest { createInstance().run { - reloadValueSet(Locale.GERMAN) - latestValueSet.first() shouldBe valueSetDE - - reloadValueSet(Locale.ENGLISH) - latestValueSet.first() shouldBe valueSetEN - } + triggerUpdateValueSet(languageCode = Locale.FRENCH) + latestValueSet.first() + }.also { it.validateValues(valueSetEN) } - verify(exactly = 0) { - valueSetsStorage.vaccinationValueSet - } - - coVerify(exactly = 1) { - vaccinationServer.getVaccinationValueSets(Locale.GERMAN) - vaccinationServer.getVaccinationValueSets(Locale.ENGLISH) + coVerify(ordering = Ordering.ORDERED) { + vaccinationServer.getVaccinationValueSets(languageCode = Locale.FRENCH) + vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } } @Test - fun `if no value set is available for specified language fall back to EN`() = runBlockingTest { - coEvery { vaccinationServer.getVaccinationValueSets(Locale.ENGLISH) } returns valueSetEN + fun `server returns nothing`() = runBlockingTest { + coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns null + coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns null createInstance().run { - reloadValueSet(Locale.GERMAN) - latestValueSet.first() shouldBe valueSetEN + triggerUpdateValueSet(languageCode = Locale.GERMAN) + latestValueSet.first() + }.also { it.validateValues(emptyValueSetEN) } - coVerify(exactly = 1) { - vaccinationServer.getVaccinationValueSets(Locale.GERMAN) - vaccinationServer.getVaccinationValueSets(Locale.ENGLISH) - } + coVerify(ordering = Ordering.ORDERED) { + vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) + vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } } @Test - fun `use local storage if server returns nothing`() = runBlockingTest { - every { valueSetsStorage.vaccinationValueSet } returns valueSetDE - - createInstance().run { - reloadValueSet(Locale.GERMAN) - latestValueSet.first() shouldBe valueSetDE - - coVerify(exactly = 1) { - valueSetsStorage.vaccinationValueSet - vaccinationServer.getVaccinationValueSets(any()) - } - } + fun `mapping is to stored version is correct`() { + valueSetDE.also { it.toStoredVaccinationValueSet().validateValues(it) } + valueSetEN.also { it.toStoredVaccinationValueSet().validateValues(it) } + emptyValueSetEN.also { it.toStoredVaccinationValueSet().validateValues(it) } } @Test - fun `user errors will not crash the app`() = runBlockingTest { - val userError = Exception("User error") - coEvery { vaccinationServer.getVaccinationValueSets(any()) } throws userError - every { valueSetsStorage.vaccinationValueSet } throws userError - + fun `clear data of server and local storage`() { createInstance().run { - reloadValueSet(Locale.GERMAN) - latestValueSet.first() shouldBe emptyValueSetDE + clear() coVerify(exactly = 1) { - valueSetsStorage.vaccinationValueSet - vaccinationServer.getVaccinationValueSets(Locale.GERMAN) - vaccinationServer.getVaccinationValueSets(Locale.ENGLISH) + vaccinationServer.clear() + valueSetsStorage.clear() } } } - @Test - fun `clear() clears server and local storage`() { - createInstance().run { - clear() + private fun VaccinationValueSet.validateValues(v2: VaccinationValueSet) { + languageCode shouldBe v2.languageCode + vp.validateValues(v2.vp) + mp.validateValues(v2.mp) + ma.validateValues(v2.ma) + } - coVerify(exactly = 1) { - vaccinationServer.clear() - valueSetsStorage.clear() - } + private fun VaccinationValueSet.ValueSet.validateValues(v2: VaccinationValueSet.ValueSet) { + items.forEachIndexed { index, item1 -> + val item2 = v2.items[index] + + item1.key shouldBe item2.key + item1.displayText shouldBe item2.displayText } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..e333cef8a7f2eb2ba48fd036fb38a1840c1c1f58 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt @@ -0,0 +1,95 @@ +package de.rki.coronawarnapp.vaccination.core.repository.storage + +import android.content.Context +import de.rki.coronawarnapp.util.serialization.SerializationModule +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.preferences.MockSharedPreferences +import java.util.Locale + +class ValueSetsStorageTest : BaseTest() { + + @MockK lateinit var context: Context + lateinit var prefs: MockSharedPreferences + + private val gson = SerializationModule().baseGson() + + private val storedValueSetDE = ValueSetsStorage.StoredVaccinationValueSet( + languageCode = Locale.GERMAN, + vp = createValueSet(key = "1119305005", displayText = "Impfstoff-Name"), + mp = createValueSet(key = "EU/1/21/1529", displayText = "Arzneimittel-Name"), + ma = createValueSet(key = "ORG-100001699", displayText = "Hersteller-Name") + ) + + private val storedValueSetEN = ValueSetsStorage.StoredVaccinationValueSet( + languageCode = Locale.ENGLISH, + vp = createValueSet(key = "1119305005", displayText = "Vaccine-Name"), + mp = createValueSet(key = "EU/1/21/1529", displayText = "MedicalProduct-Name"), + ma = createValueSet(key = "ORG-100001699", displayText = "Manufactorer-Name") + ) + + private fun createValueSet(key: String, displayText: String): ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet { + val item = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem( + key = key, + displayText = displayText + ) + return ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = listOf(item)) + } + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + prefs = MockSharedPreferences() + every { context.getSharedPreferences("valuesets_localdata", Context.MODE_PRIVATE) } returns prefs + } + + private fun createInstance() = ValueSetsStorage( + context = context, + gson = gson + ) + + @Test + fun `Default value is empty value set`() { + createInstance().valueSet.value.run { + languageCode shouldBe Locale.ENGLISH + vp.items shouldBe emptyList() + mp.items shouldBe emptyList() + ma.items shouldBe emptyList() + } + } + + @Test + fun `Clear resets value set`() { + createInstance().run { + valueSet.update { storedValueSetDE } + clear() + + valueSet.value.also { + it.languageCode shouldBe Locale.ENGLISH + it.vp.items shouldBe emptyList() + it.mp.items shouldBe emptyList() + it.ma.items shouldBe emptyList() + } + } + } + + @Test + fun `Updates values`() = runBlockingTest { + createInstance().valueSet.run { + update { storedValueSetDE } + value shouldBe storedValueSetDE + flow.first() shouldBe storedValueSetDE + + update { storedValueSetEN } + value shouldBe storedValueSetEN + flow.first() shouldBe storedValueSetEN + } + } +} diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt index 27b4fe9757b14077fea0e63b813f732064aeab88..c1b802516a2f5aa590dd3cf5ba6fcbd6d23e5906 100644 --- a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt @@ -31,7 +31,6 @@ class DebugOptionsFragmentViewModelTest : BaseTestInstrumentation() { every { environmentSetup.verificationCdnUrl } returns "verificationUrl" every { environmentSetup.dataDonationCdnUrl } returns "dataDonationUrl" every { environmentSetup.logUploadServerUrl } returns "logUploadServerUrl" - every { environmentSetup.vaccinationCdnUrl } returns "vaccinationCdnUrl" every { environmentSetup.crowdNotifierPublicKey } returns "crowdNotifierPublicKey" every { environmentSetup.appConfigPublicKey } returns "appConfigPublicKey" diff --git a/prod_environments.json b/prod_environments.json index 8c50a11f3fd336642c98f2cdbc19deb6c17aac4c..09d9015c150489eb2dac757a9dfbe5d25f476926 100644 --- a/prod_environments.json +++ b/prod_environments.json @@ -6,8 +6,6 @@ "VERIFICATION_CDN_URL": "https://verification.coronawarn.app", "DATA_DONATION_CDN_URL": "https://data.coronawarn.app", "LOG_UPLOAD_SERVER_URL": "https://logupload.coronawarn.app", - "VACCINATION_PROOF_SERVER_URL": "https://api.recertify.ubirch.com", - "VACCINATION_CDN_URL": "https://placeholder", "SAFETYNET_API_KEY": "placeholder", "PUB_KEYS_SIGNATURE_VERIFICATION": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0zK7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5Xg==", "CROWD_NOTIFIER_PUBLIC_KEY": "gwLMzE153tQwAOf2MZoUXXfzWTdlSpfS99iZffmcmxOG9njSK4RTimFOFwDh6t0Tyw8XR01ugDYjtuKwjjuK49Oh83FWct6XpefPi9Skjxvvz53i9gaMmUEc96pbtoaA"