From 3f500b32bd9ff5270f9e5c96ddf9cad82fe0291c Mon Sep 17 00:00:00 2001 From: Sergejs Luhmirins Date: Tue, 7 May 2024 17:13:41 +0300 Subject: [PATCH 1/2] MS-438 Add capture event ID to the step result data --- .../face/capture/FaceCaptureResult.kt | 1 + .../capture/screens/FaceCaptureViewModel.kt | 15 ++++---- .../cache/OrchestratorCacheIntegrationTest.kt | 2 + ...apStepsForLastBiometricEnrolUseCaseTest.kt | 38 +++++++++++-------- .../capture/FingerprintCaptureResult.kt | 1 + .../screen/FingerprintCaptureViewModel.kt | 2 + 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt index aa0ac896d7..649d24b57a 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt @@ -11,6 +11,7 @@ data class FaceCaptureResult( @Keep data class Item( + val captureEventId: String?, val index: Int, val sample: Sample?, ) : Serializable diff --git a/face/capture/src/main/java/com/simprints/face/capture/screens/FaceCaptureViewModel.kt b/face/capture/src/main/java/com/simprints/face/capture/screens/FaceCaptureViewModel.kt index 65a68554ef..cacbcc9247 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/screens/FaceCaptureViewModel.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/screens/FaceCaptureViewModel.kt @@ -62,7 +62,6 @@ internal class FaceCaptureViewModel @Inject constructor( get() = _finishFlowEvent private val _finishFlowEvent = MutableLiveData>() - val invalidLicense: LiveData get() = _invalidLicense private val _invalidLicense = MutableLiveData() @@ -106,12 +105,13 @@ internal class FaceCaptureViewModel @Inject constructor( val items = faceDetections.mapIndexed { index, detection -> FaceCaptureResult.Item( - index, - FaceCaptureResult.Sample( - detection.id, - detection.face?.template ?: ByteArray(0), - detection.securedImageRef, - detection.face?.format ?: "", + captureEventId = detection.id, + index = index, + sample = FaceCaptureResult.Sample( + faceId = detection.id, + template = detection.face?.template ?: ByteArray(0), + imageRef = detection.securedImageRef, + format = detection.face?.format ?: "", ) ) } @@ -159,5 +159,4 @@ internal class FaceCaptureViewModel @Inject constructor( eventReporter.addCaptureConfirmationEvent(startTime, isContinue) } - } diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt index fd5ea9f1ff..6f195bdccf 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt @@ -10,6 +10,7 @@ import com.simprints.feature.orchestrator.steps.Step import com.simprints.feature.orchestrator.steps.StepId import com.simprints.feature.orchestrator.steps.StepStatus import com.simprints.fingerprint.capture.FingerprintCaptureResult +import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 import com.simprints.infra.security.SecurityManager import io.mockk.MockKAnnotations import io.mockk.every @@ -62,6 +63,7 @@ class OrchestratorCacheIntegrationTest { result = FingerprintCaptureResult( results = listOf( FingerprintCaptureResult.Item( + captureEventId = GUID1, identifier = IFingerIdentifier.LEFT_THUMB, sample = null ) diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCaseTest.kt index 9ff8a7ed4f..ab36dd7356 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCaseTest.kt @@ -11,6 +11,7 @@ import com.simprints.infra.config.store.models.Finger import com.simprints.matcher.FaceMatchResult import com.simprints.matcher.FingerprintMatchResult import com.simprints.core.domain.fingerprint.IFingerIdentifier +import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 import org.junit.Before import org.junit.Test import java.io.Serializable @@ -48,13 +49,17 @@ internal class MapStepsForLastBiometricEnrolUseCaseTest { val result = useCase(listOf( FaceCaptureResult( results = listOf( - FaceCaptureResult.Item(0, null), - FaceCaptureResult.Item(0, FaceCaptureResult.Sample( - faceId = "faceId", - template = byteArrayOf(), - imageRef = null, - format = "format" - )) + FaceCaptureResult.Item(captureEventId = null, index = 0, sample = null), + FaceCaptureResult.Item( + captureEventId = GUID1, + index = 0, + sample = FaceCaptureResult.Sample( + faceId = "faceId", + template = byteArrayOf(), + imageRef = null, + format = "format" + ) + ) ), ) )) @@ -81,15 +86,18 @@ internal class MapStepsForLastBiometricEnrolUseCaseTest { val result = useCase(listOf( FingerprintCaptureResult( results = listOf( - FingerprintCaptureResult.Item(IFingerIdentifier.LEFT_THUMB, null), + FingerprintCaptureResult.Item(null, IFingerIdentifier.LEFT_THUMB, null), FingerprintCaptureResult.Item( - IFingerIdentifier.RIGHT_THUMB, FingerprintCaptureResult.Sample( - fingerIdentifier = IFingerIdentifier.RIGHT_THUMB, - template = byteArrayOf(), - templateQualityScore = 0, - imageRef = null, - format = "format" - )) + identifier = IFingerIdentifier.RIGHT_THUMB, + captureEventId = GUID1, + sample = FingerprintCaptureResult.Sample( + fingerIdentifier = IFingerIdentifier.RIGHT_THUMB, + template = byteArrayOf(), + templateQualityScore = 0, + imageRef = null, + format = "format" + ) + ) ), ) )) diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt index e7d245e820..5d430ea6e8 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt @@ -12,6 +12,7 @@ data class FingerprintCaptureResult( @Keep data class Item( + val captureEventId: String?, val identifier: IFingerIdentifier, val sample: Sample?, ) : Serializable diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt index b89c832f5d..fbea0da748 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt @@ -53,6 +53,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import java.util.UUID import javax.inject.Inject import kotlin.math.min @@ -600,6 +601,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( val resultItems = collectedFingers.map { (captureId, collectedFinger) -> FingerprintCaptureResult.Item( identifier = captureId.finger, + captureEventId = captureEventIds[captureId], sample = FingerprintCaptureResult.Sample( fingerIdentifier = captureId.finger, template = collectedFinger.scanResult.template, From 20285c35f47a184b38a2e267a13710467cf0387d Mon Sep 17 00:00:00 2001 From: Sergejs Luhmirins Date: Tue, 7 May 2024 17:14:27 +0300 Subject: [PATCH 2/2] MS-438 Compose CreatePerson event only based on information from step results --- .../usecases/CreatePersonEventUseCase.kt | 81 +++++++------------ .../usecases/CreatePersonEventUseCaseTest.kt | 58 +++++++------ .../sync/down/tasks/SubjectFactoryTest.kt | 3 + 3 files changed, 60 insertions(+), 82 deletions(-) diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCase.kt index f14eea7b11..e6b2e01bc9 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCase.kt @@ -5,20 +5,16 @@ import com.simprints.core.domain.face.uniqueId import com.simprints.core.domain.fingerprint.FingerprintSample import com.simprints.core.domain.fingerprint.uniqueId import com.simprints.core.tools.time.TimeHelper -import com.simprints.core.tools.utils.EncodingUtils import com.simprints.face.capture.FaceCaptureResult import com.simprints.fingerprint.capture.FingerprintCaptureResult import com.simprints.infra.events.SessionEventRepository import com.simprints.infra.events.event.domain.models.PersonCreationEvent -import com.simprints.infra.events.event.domain.models.face.FaceCaptureBiometricsEvent -import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureBiometricsEvent import java.io.Serializable import javax.inject.Inject internal class CreatePersonEventUseCase @Inject constructor( private val eventRepository: SessionEventRepository, private val timeHelper: TimeHelper, - private val encodingUtils: EncodingUtils, ) { suspend operator fun invoke(results: List) { @@ -27,70 +23,53 @@ internal class CreatePersonEventUseCase @Inject constructor( // If a personCreationEvent is already in the current session, // we don' want to add it again (the capture steps would still be the same) if (sessionEvents.none { it is PersonCreationEvent }) { - val faceCaptureBiometricsEvents = sessionEvents.filterIsInstance() - val fingerprintCaptureBiometricsEvents = sessionEvents.filterIsInstance() + val faceCaptures = extractFaceCaptures(results) + val fingerprintCaptures = extractFingerprintCaptures(results) - val faceSamples = extractFaceSamples(results) - val fingerprintSamples = extractFingerprintSamples(results) + val personCreationEvent = build(faceCaptures, fingerprintCaptures) - val personCreationEvent = build( - faceCaptureBiometricsEvents, - fingerprintCaptureBiometricsEvents, - faceSamples, - fingerprintSamples - ) if (personCreationEvent.hasBiometricData()) { eventRepository.addOrUpdateEvent(personCreationEvent) } } } - private fun extractFaceSamples(responses: List) = responses + private fun extractFaceCaptures(responses: List) = responses .filterIsInstance() .flatMap { it.results } - .mapNotNull { it.sample } - .map { FaceSample(it.template, it.format) } - private fun extractFingerprintSamples(responses: List) = responses + private fun extractFingerprintCaptures(responses: List) = responses .filterIsInstance() .flatMap { it.results } - .mapNotNull { it.sample } - .map { FingerprintSample(it.fingerIdentifier, it.template, it.templateQualityScore, it.format) } private fun build( - faceCaptureBiometricsEvents: List, - fingerprintCaptureBiometricsEvents: List, - faceSamplesForPersonCreation: List?, - fingerprintSamplesForPersonCreation: List? - ) = PersonCreationEvent( - startTime = timeHelper.now(), - fingerprintCaptureIds = extractFingerprintCaptureEventIdsBasedOnPersonTemplate( - fingerprintCaptureBiometricsEvents, - fingerprintSamplesForPersonCreation?.map { encodingUtils.byteArrayToBase64(it.template) } - ), - fingerprintReferenceId = fingerprintSamplesForPersonCreation?.uniqueId(), - faceCaptureIds = extractFaceCaptureEventIdsBasedOnPersonTemplate( - faceCaptureBiometricsEvents, - faceSamplesForPersonCreation?.map { encodingUtils.byteArrayToBase64(it.template) } - ), - faceReferenceId = faceSamplesForPersonCreation?.uniqueId() - ) + faceSamplesForPersonCreation: List, + fingerprintSamplesForPersonCreation: List, + ): PersonCreationEvent { + val fingerprintCaptureIds = fingerprintSamplesForPersonCreation + .mapNotNull { it.captureEventId } + .ifEmpty { null } + val fingerprintReferenceId = fingerprintSamplesForPersonCreation + .mapNotNull { it.sample } + .map { FingerprintSample(it.fingerIdentifier, it.template, it.templateQualityScore, it.format) } + .uniqueId() - private fun extractFingerprintCaptureEventIdsBasedOnPersonTemplate( - captureEvents: List, - personTemplates: List? - ): List? = captureEvents - .filter { personTemplates?.contains(it.payload.fingerprint.template) == true } - .map { it.payload.id } - .ifEmpty { null } + val faceCaptureIds = faceSamplesForPersonCreation + .mapNotNull { it.captureEventId } + .ifEmpty { null } + val faceReferenceId = faceSamplesForPersonCreation + .mapNotNull { it.sample } + .map { FaceSample(it.template, it.format) } + .uniqueId() - private fun extractFaceCaptureEventIdsBasedOnPersonTemplate( - captureEvents: List, - personTemplates: List? - ): List? = captureEvents - .filter { personTemplates?.contains(it.payload.face.template) == true } - .map { it.payload.id } - .ifEmpty { null } + return PersonCreationEvent( + startTime = timeHelper.now(), + fingerprintCaptureIds = fingerprintCaptureIds, + fingerprintReferenceId = fingerprintReferenceId, + faceCaptureIds = faceCaptureIds, + faceReferenceId = faceReferenceId, + ) + } private fun PersonCreationEvent.hasBiometricData() = payload.fingerprintCaptureIds?.isNotEmpty() == true || payload.faceCaptureIds?.isNotEmpty() == true diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCaseTest.kt index 9e1424ea0e..2f4259b30c 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/CreatePersonEventUseCaseTest.kt @@ -1,18 +1,14 @@ package com.simprints.feature.orchestrator.usecases import com.google.common.truth.Truth.assertThat +import com.simprints.core.domain.fingerprint.IFingerIdentifier import com.simprints.core.tools.time.TimeHelper -import com.simprints.core.tools.utils.EncodingUtils +import com.simprints.core.tools.time.Timestamp import com.simprints.face.capture.FaceCaptureResult import com.simprints.fingerprint.capture.FingerprintCaptureResult +import com.simprints.infra.events.SessionEventRepository import com.simprints.infra.events.event.domain.models.PersonCreationEvent -import com.simprints.infra.events.event.domain.models.face.FaceCaptureBiometricsEvent -import com.simprints.infra.events.event.domain.models.face.FaceCaptureEvent import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureBiometricsEvent -import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureEvent -import com.simprints.core.domain.fingerprint.IFingerIdentifier -import com.simprints.core.tools.time.Timestamp -import com.simprints.infra.events.SessionEventRepository import com.simprints.testtools.common.coroutines.TestCoroutineRule import io.mockk.MockKAnnotations import io.mockk.coEvery @@ -36,9 +32,6 @@ internal class CreatePersonEventUseCaseTest { @MockK lateinit var timeHelper: TimeHelper - @MockK - lateinit var encodingUtils: EncodingUtils - private lateinit var useCase: CreatePersonEventUseCase @Before @@ -46,20 +39,17 @@ internal class CreatePersonEventUseCaseTest { MockKAnnotations.init(this, relaxed = true) every { timeHelper.now() } returns Timestamp(0L) - every { encodingUtils.byteArrayToBase64(any()) } returns TEMPLATE coEvery { eventRepository.getCurrentSessionScope() } returns mockk { every { id } returns "sessionId" } - useCase = CreatePersonEventUseCase(eventRepository, timeHelper, encodingUtils) + useCase = CreatePersonEventUseCase(eventRepository, timeHelper) } @Test fun `Does not create event if has person creation in session`() = runTest { coEvery { eventRepository.getEventsInCurrentSession() } returns listOf( - mockk(), - mockk(), mockk(), ) @@ -70,10 +60,7 @@ internal class CreatePersonEventUseCaseTest { @Test fun `Does not create event if no biometric data`() = runTest { - coEvery { eventRepository.getEventsInCurrentSession() } returns listOf( - mockk(), - mockk(), - ) + coEvery { eventRepository.getEventsInCurrentSession() } returns listOf() useCase(listOf()) @@ -82,18 +69,13 @@ internal class CreatePersonEventUseCaseTest { @Test fun `Create event if there is face biometric data`() = runTest { - coEvery { eventRepository.getEventsInCurrentSession() } returns listOf( - mockk { - every { payload.id } returns "eventFaceId1" - every { payload.face.template } returns TEMPLATE - }, - ) + coEvery { eventRepository.getEventsInCurrentSession() } returns listOf() useCase(listOf(FaceCaptureResult(listOf(createFaceCaptureResultItem())))) coVerify { eventRepository.addOrUpdateEvent(withArg { - assertThat(it.payload.faceCaptureIds).isEqualTo(listOf("eventFaceId1")) + assertThat(it.payload.faceCaptureIds).isEqualTo(listOf(FACE_ID)) }) } } @@ -102,7 +84,7 @@ internal class CreatePersonEventUseCaseTest { fun `Create event if there is fingerprint biometric data`() = runTest { coEvery { eventRepository.getEventsInCurrentSession() } returns listOf( mockk { - every { payload.id } returns "eventFinger1" + every { payload.id } returns FINGER_ID every { payload.fingerprint.template } returns TEMPLATE }, ) @@ -111,22 +93,36 @@ internal class CreatePersonEventUseCaseTest { coVerify { eventRepository.addOrUpdateEvent(withArg { - assertThat(it.payload.fingerprintCaptureIds).isEqualTo(listOf("eventFinger1")) + assertThat(it.payload.fingerprintCaptureIds).isEqualTo(listOf(FINGER_ID)) }) } } private fun createFingerprintCaptureResultItem() = FingerprintCaptureResult.Item( - IFingerIdentifier.RIGHT_THUMB, - FingerprintCaptureResult.Sample(IFingerIdentifier.RIGHT_THUMB, byteArrayOf(), 0, null, "format") + captureEventId = FINGER_ID, + identifier = IFingerIdentifier.RIGHT_THUMB, + sample = FingerprintCaptureResult.Sample( + IFingerIdentifier.RIGHT_THUMB, + TEMPLATE.toByteArray(), + 0, + null, + "format" + ) ) + private fun createFaceCaptureResultItem() = - FaceCaptureResult.Item(0, FaceCaptureResult.Sample("faceId", byteArrayOf(), null, "format")) + FaceCaptureResult.Item( + captureEventId = FACE_ID, + index = 0, + sample = FaceCaptureResult.Sample(FACE_ID, TEMPLATE.toByteArray(), null, "format") + ) companion object { - const val TEMPLATE = "template" + private const val TEMPLATE = "template" + private const val FINGER_ID = "eventFinger1" + private const val FACE_ID = "eventFinger1" } } diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SubjectFactoryTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SubjectFactoryTest.kt index a60f45d86f..4a4d4a92dd 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SubjectFactoryTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SubjectFactoryTest.kt @@ -16,6 +16,7 @@ import com.simprints.infra.events.event.domain.models.subject.FaceTemplate import com.simprints.infra.events.event.domain.models.subject.FingerprintReference import com.simprints.infra.events.event.domain.models.subject.FingerprintTemplate import com.simprints.core.domain.fingerprint.IFingerIdentifier +import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK @@ -153,6 +154,7 @@ class SubjectFactoryTest { moduleId = expected.moduleId, fingerprintResponse = FingerprintCaptureResult(listOf( FingerprintCaptureResult.Item( + captureEventId = GUID1, identifier = IDENTIFIER, sample = FingerprintCaptureResult.Sample( template = BASE_64_BYTES, @@ -165,6 +167,7 @@ class SubjectFactoryTest { )), faceResponse = FaceCaptureResult(listOf( FaceCaptureResult.Item( + captureEventId = GUID1, index = 0, sample = FaceCaptureResult.Sample( template = BASE_64_BYTES,