Skip to content
Snippets Groups Projects
Unverified Commit b268937d authored by Matthias Urhahn's avatar Matthias Urhahn Committed by GitHub
Browse files

Introduce `CertificateContainerId` as common identifier when trying to...

Introduce `CertificateContainerId` as common identifier when trying to load/modify/delete certificate data via repositories. (#3504)

Co-authored-by: default avatarMohamed Metwalli <mohamed.metwalli@sap.com>
parent b76ae1a2
No related branches found
No related tags found
No related merge requests found
Showing
with 103 additions and 35 deletions
......@@ -15,8 +15,8 @@ import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier
import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier
import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsViewModel
import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment
import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragmentArgs
......@@ -43,7 +43,9 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
@MockK lateinit var vaccinationDetailsViewModel: CovidCertificateDetailsViewModel
@MockK lateinit var certificatePersonIdentifier: CertificatePersonIdentifier
private val args = TestCertificateDetailsFragmentArgs("testCertificateIdentifier").toBundle()
private val args = TestCertificateDetailsFragmentArgs(
containerId = TestCertificateContainerId("testCertificateIdentifier")
).toBundle()
@Before
fun setUp() {
......@@ -53,7 +55,7 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
setupMockViewModel(
object : CovidCertificateDetailsViewModel.Factory {
override fun create(testCertificateIdentifier: TestCertificateIdentifier):
override fun create(containerId: TestCertificateContainerId):
CovidCertificateDetailsViewModel = vaccinationDetailsViewModel
}
)
......@@ -86,6 +88,8 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
val testDate = DateTime.parse("12.05.2021 19:00", formatter).toInstant()
return MutableLiveData(
object : TestCertificate {
override val containerId: TestCertificateContainerId
get() = TestCertificateContainerId("identifier")
override val targetName: String
get() = "Schneider, Andrea"
override val testType: String
......
......@@ -13,6 +13,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate
import io.mockk.MockKAnnotations
import io.mockk.every
......@@ -36,7 +37,9 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
@MockK lateinit var vaccinationDetailsViewModel: VaccinationDetailsViewModel
private val args = VaccinationDetailsFragmentArgs("vaccinationCertificateId").toBundle()
private val args = VaccinationDetailsFragmentArgs(
containerId = VaccinationCertificateContainerId("vaccinationCertificateId")
).toBundle()
@Before
fun setUp() {
......@@ -46,7 +49,9 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
setupMockViewModel(
object : VaccinationDetailsViewModel.Factory {
override fun create(certificateId: String): VaccinationDetailsViewModel = vaccinationDetailsViewModel
override fun create(
containerId: VaccinationCertificateContainerId
): VaccinationDetailsViewModel = vaccinationDetailsViewModel
}
)
}
......
......@@ -11,6 +11,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson
import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.COMPLETE
import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.IMMUNITY
......@@ -176,7 +177,7 @@ internal class VaccinationListFragmentTest : BaseUITest() {
vaccinationStatus: VaccinatedPerson.Status,
vaccinatedAt: LocalDate = LocalDate.parse("12.04.2021", formatter)
) = VaccinationListVaccinationCardItem(
vaccinationCertificateId = "vaccinationCertificateId",
containerId = VaccinationCertificateContainerId("vaccinationCertificateId"),
doseNumber = doseNumber,
totalSeriesOfDoses = totalSeriesOfDoses,
vaccinatedAt = vaccinatedAt.toDayFormat(),
......
package de.rki.coronawarnapp.covidcertificate.common.certificate
import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateContainerId
import org.joda.time.Instant
import org.joda.time.LocalDate
......@@ -21,4 +22,9 @@ interface CwaCovidCertificate {
val certificateIssuer: String
val certificateCountry: String
val certificateId: String
/**
* The ID of the container holding this certificate in the CWA.
*/
val containerId: CertificateContainerId
}
package de.rki.coronawarnapp.covidcertificate.common.repository
import android.os.Parcelable
/**
* A unique identifier for a container holding a certificate in the CWA.
* As there is a 1:1 match between container and certificates, it's also a unique ID for the certificate.
* It's the ID you used pass around in the UI to find it/modify/delete it via a repository.
* We can't use uniqueCertificateId because we also handle `TestCertificate`'s that have not been retrieved yet.
*/
sealed class CertificateContainerId : Parcelable {
abstract val identifier: String
}
package de.rki.coronawarnapp.covidcertificate.common.repository
interface CertificateRepoContainer {
val containerId: CertificateContainerId
}
package de.rki.coronawarnapp.covidcertificate.common.repository
import kotlinx.parcelize.Parcelize
@Parcelize
data class RecoveryCertificateContainerId(override val identifier: String) : CertificateContainerId()
package de.rki.coronawarnapp.covidcertificate.common.repository
import kotlinx.parcelize.Parcelize
@Parcelize
data class TestCertificateContainerId(override val identifier: String) : CertificateContainerId()
package de.rki.coronawarnapp.covidcertificate.common.repository
import kotlinx.parcelize.Parcelize
@Parcelize
data class VaccinationCertificateContainerId(override val identifier: String) : CertificateContainerId()
......@@ -71,7 +71,7 @@ class DccQrCodeScanFragment :
doNavigate(
DccQrCodeScanFragmentDirections
.actionDccQrCodeScanFragmentToVaccinationDetailsFragment(
event.certificateId
event.containerId
)
)
}
......@@ -82,7 +82,7 @@ class DccQrCodeScanFragment :
doNavigate(
DccQrCodeScanFragmentDirections
.actionDccQrCodeScanFragmentToTestCertificateDetailsFragment(
event.certificateId
event.containerId
)
)
}
......
......@@ -3,6 +3,9 @@ package de.rki.coronawarnapp.covidcertificate.common.scan
import com.journeyapps.barcodescanner.BarcodeResult
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.recovery.core.RecoveryCertificateRepository
import de.rki.coronawarnapp.covidcertificate.recovery.core.qrcode.RecoveryCertificateQRCode
import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository
......@@ -42,8 +45,8 @@ class DccQrCodeScanViewModel @AssistedInject constructor(
}
private suspend fun registerVaccinationCertificate(qrCode: VaccinationCertificateQRCode) {
val certificate = vaccinationRepository.registerVaccination(qrCode)
event.postValue(Event.VaccinationQrCodeScanSucceeded(certificate.certificateId))
val certificate = vaccinationRepository.registerCertificate(qrCode)
event.postValue(Event.VaccinationQrCodeScanSucceeded(certificate.containerId))
}
private suspend fun registerTestCertificate(qrCode: TestCertificateQRCode) {
......@@ -67,9 +70,9 @@ class DccQrCodeScanViewModel @AssistedInject constructor(
sealed class Event {
object QrCodeScanInProgress : Event()
data class VaccinationQrCodeScanSucceeded(val certificateId: String) : Event()
data class TestQrCodeScanSucceeded(val certificateId: String) : Event()
data class RecoveryQrCodeScanSucceeded(val certificateId: String) : Event()
data class VaccinationQrCodeScanSucceeded(val containerId: VaccinationCertificateContainerId) : Event()
data class TestQrCodeScanSucceeded(val containerId: TestCertificateContainerId) : Event()
data class RecoveryQrCodeScanSucceeded(val containerId: RecoveryCertificateContainerId) : Event()
}
@AssistedFactory
......
......@@ -4,6 +4,7 @@ import dagger.Reusable
import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier
import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate
import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.recovery.core.RecoveryCertificateRepository
import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository
......@@ -14,6 +15,7 @@ import kotlinx.coroutines.flow.map
import org.joda.time.Instant
import org.joda.time.LocalDate
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
// Aggregate the certificates and sort them
......@@ -78,6 +80,8 @@ class PersonCertificatesProvider @Inject constructor(
isUpdating: Boolean = false
) =
object : TestCertificate {
override val containerId: TestCertificateContainerId
get() = TestCertificateContainerId(UUID.randomUUID().toString())
override val targetName: String
get() = "targetName"
override val testType: String
......
......@@ -61,7 +61,7 @@ class PersonOverviewFragment : Fragment(R.layout.person_overview_fragment), Auto
.setNegativeButton(R.string.test_certificate_delete_dialog_cancel_button) { _, _ -> }
.setCancelable(false)
.setPositiveButton(R.string.test_certificate_delete_dialog_confirm_button) { _, _ ->
viewModel.deleteTestCertificate(event.certificateId)
viewModel.deleteTestCertificate(event.containerId)
}
.show()
......
package de.rki.coronawarnapp.covidcertificate.person.ui.overview
import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
sealed class PersonOverviewFragmentEvents
data class ShowRefreshErrorDialog(val error: Throwable) : PersonOverviewFragmentEvents()
data class ShowDeleteDialog(val certificateId: String) : PersonOverviewFragmentEvents()
data class ShowDeleteDialog(val containerId: TestCertificateContainerId) : PersonOverviewFragmentEvents()
data class OpenPersonDetailsFragment(val personIdentifier: String) : PersonOverviewFragmentEvents()
object ScanQrCode : PersonOverviewFragmentEvents()
object OpenAppDeviceSettings : PersonOverviewFragmentEvents()
......@@ -8,15 +8,15 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.contactdiary.util.getLocale
import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificates
import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificatesProvider
import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CameraPermissionCard
import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CertificatesItem
import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CovidTestCertificatePendingCard
import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CameraPermissionCard
import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificateCard
import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository
import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier
import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository
import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator
import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.permission.CameraPermissionProvider
......@@ -64,15 +64,15 @@ class PersonOverviewViewModel @AssistedInject constructor(
wrappers
.filter { !it.seenByUser && !it.isCertificateRetrievalPending }
.forEach {
testCertificateRepository.markCertificateAsSeenByUser(it.identifier)
testCertificateRepository.markCertificateAsSeenByUser(it.containerId)
}
}
.map { }
.catch { Timber.w("Failed to mark certificates as seen.") }
.asLiveData2()
fun deleteTestCertificate(identifier: TestCertificateIdentifier) = launch {
testCertificateRepository.deleteCertificate(identifier)
fun deleteTestCertificate(containerId: TestCertificateContainerId) = launch {
testCertificateRepository.deleteCertificate(containerId)
}
fun onScanQrCode() = events.postValue(ScanQrCode)
......@@ -113,8 +113,8 @@ class PersonOverviewViewModel @AssistedInject constructor(
add(
CovidTestCertificatePendingCard.Item(
certificate = certificate,
onRetryAction = { refreshCertificate(certificate.certificateId) },
onDeleteAction = { events.postValue(ShowDeleteDialog(certificate.certificateId)) }
onRetryAction = { refreshCertificate(certificate.containerId) },
onDeleteAction = { events.postValue(ShowDeleteDialog(certificate.containerId)) }
)
)
}
......@@ -149,9 +149,9 @@ class PersonOverviewViewModel @AssistedInject constructor(
null
}
fun refreshCertificate(identifier: TestCertificateIdentifier) =
fun refreshCertificate(containerId: TestCertificateContainerId) =
launch {
val error = testCertificateRepository.refresh(identifier).mapNotNull { it.error }.singleOrNull()
val error = testCertificateRepository.refresh(containerId).mapNotNull { it.error }.singleOrNull()
error?.let { events.postValue(ShowRefreshErrorDialog(error)) }
}
......
package de.rki.coronawarnapp.covidcertificate.recovery.core
import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate
import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId
import org.joda.time.LocalDate
interface RecoveryCertificate : CwaCovidCertificate {
override val containerId: RecoveryCertificateContainerId
val testedPositiveOn: LocalDate
val validFrom: LocalDate
val validUntil: LocalDate
......
package de.rki.coronawarnapp.covidcertificate.recovery.core
import de.rki.coronawarnapp.covidcertificate.common.certificate.DccQrCodeExtractor
import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.recovery.core.qrcode.RecoveryCertificateQRCode
import de.rki.coronawarnapp.covidcertificate.recovery.core.storage.RecoveryCertificateContainer
import de.rki.coronawarnapp.covidcertificate.recovery.core.storage.RecoveryCertificateIdentifier
import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository
import de.rki.coronawarnapp.util.coroutine.AppScope
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
......@@ -24,13 +24,13 @@ class RecoveryCertificateRepository @Inject constructor(
val certificates: Flow<Set<RecoveryCertificateWrapper>> = flowOf(emptySet())
suspend fun requestCertificate(qrCode: RecoveryCertificateQRCode): RecoveryCertificateContainer {
Timber.tag(TAG).d("requestCertificate(qrCode=%s)", qrCode)
suspend fun registerCertificate(qrCode: RecoveryCertificateQRCode): RecoveryCertificateContainer {
Timber.tag(TAG).d("registerCertificate(qrCode=%s)", qrCode)
throw NotImplementedError()
}
suspend fun deleteCertificate(identifier: RecoveryCertificateIdentifier) {
Timber.tag(TAG).d("deleteTestCertificate(identifier=%s)", identifier)
suspend fun deleteCertificate(containerId: RecoveryCertificateContainerId): RecoveryCertificateContainer? {
Timber.tag(TAG).d("deleteCertificate(containerId=%s)", containerId)
throw NotImplementedError()
}
......
package de.rki.coronawarnapp.covidcertificate.recovery.core
import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.recovery.core.storage.RecoveryCertificateContainer
import de.rki.coronawarnapp.covidcertificate.recovery.core.storage.RecoveryCertificateIdentifier
import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSets
data class RecoveryCertificateWrapper(
......@@ -9,7 +9,7 @@ data class RecoveryCertificateWrapper(
private val container: RecoveryCertificateContainer
) {
val identifier: RecoveryCertificateIdentifier = container.identifier
val containerId: RecoveryCertificateContainerId get() = container.containerId
val isUpdatingData = container.isUpdatingData
......
......@@ -6,6 +6,8 @@ import de.rki.coronawarnapp.covidcertificate.common.certificate.DccQrCodeExtract
import de.rki.coronawarnapp.covidcertificate.common.certificate.DccV1Parser.Mode
import de.rki.coronawarnapp.covidcertificate.common.certificate.RecoveryDccV1
import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateRepoContainer
import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId
import de.rki.coronawarnapp.covidcertificate.recovery.core.RecoveryCertificate
import de.rki.coronawarnapp.covidcertificate.recovery.core.qrcode.RecoveryCertificateQRCode
import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.TestCertificateValueSets
......@@ -17,7 +19,7 @@ data class RecoveryCertificateContainer(
internal val data: StoredRecoveryCertificateData,
private val qrCodeExtractor: DccQrCodeExtractor,
val isUpdatingData: Boolean = false,
) : StoredRecoveryCertificate by data {
) : StoredRecoveryCertificate by data, CertificateRepoContainer {
@delegate:Transient
private val certificateData: DccData<RecoveryDccV1> by lazy {
......@@ -31,6 +33,9 @@ data class RecoveryCertificateContainer(
}
}
override val containerId: RecoveryCertificateContainerId
get() = RecoveryCertificateContainerId(data.identifier)
val certificateId: String
get() = certificateData.certificate.recovery.uniqueCertificateIdentifier
......@@ -43,6 +48,9 @@ data class RecoveryCertificateContainer(
val recoveryCertificate = certificate.recovery
return object : RecoveryCertificate {
override val containerId: RecoveryCertificateContainerId
get() = this@RecoveryCertificateContainer.containerId
override val personIdentifier: CertificatePersonIdentifier
get() = certificate.personIdentifier
......
package de.rki.coronawarnapp.covidcertificate.recovery.core.storage
typealias RecoveryCertificateIdentifier = String
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment