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 fb676fee61..391533d05b 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 @@ -7,8 +7,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.simprints.core.DeviceID import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture import com.simprints.core.livedata.LiveDataEvent import com.simprints.core.livedata.LiveDataEventWithContent import com.simprints.core.livedata.send @@ -79,9 +79,9 @@ internal class FaceCaptureViewModel @Inject constructor( get() = _unexpectedErrorEvent private val _unexpectedErrorEvent = MutableLiveData() - val finishFlowEvent: LiveData> + val finishFlowEvent: LiveData> get() = _finishFlowEvent - private val _finishFlowEvent = MutableLiveData>() + private val _finishFlowEvent = MutableLiveData>() val invalidLicense: LiveData get() = _invalidLicense @@ -179,18 +179,21 @@ internal class FaceCaptureViewModel @Inject constructor( saveFaceDetections() } - val items = faceDetections.mapIndexed { index, detection -> - CaptureSample( + val items = faceDetections.map { detection -> + BiometricTemplateCapture( captureEventId = detection.id, template = detection.face?.template ?: ByteArray(0), - format = detection.face?.format ?: "", - modality = Modality.FACE, ) } val referenceId = UUID.randomUUID().toString() eventReporter.addBiometricReferenceCreationEvents(referenceId, items.map { it.captureEventId }) - _finishFlowEvent.send(CaptureIdentity(referenceId, Modality.FACE, items)) + val format = faceDetections + .firstOrNull() + ?.face + ?.format + .orEmpty() + _finishFlowEvent.send(BiometricReferenceCapture(referenceId, Modality.FACE, format, items)) } } diff --git a/face/infra/base-bio-sdk/src/main/java/com/simprints/face/infra/basebiosdk/matching/FaceMatcher.kt b/face/infra/base-bio-sdk/src/main/java/com/simprints/face/infra/basebiosdk/matching/FaceMatcher.kt index 78892c5adf..864c8bede9 100644 --- a/face/infra/base-bio-sdk/src/main/java/com/simprints/face/infra/basebiosdk/matching/FaceMatcher.kt +++ b/face/infra/base-bio-sdk/src/main/java/com/simprints/face/infra/basebiosdk/matching/FaceMatcher.kt @@ -1,10 +1,10 @@ package com.simprints.face.infra.basebiosdk.matching -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord abstract class FaceMatcher( - open val probeSamples: List, + open val probeReference: BiometricReferenceCapture, ) : AutoCloseable { /** * Get highest comparison score for matching candidate template against samples @@ -12,5 +12,5 @@ abstract class FaceMatcher( * @param candidate * @return the highest comparison score */ - abstract suspend fun getHighestComparisonScoreForCandidate(candidate: Identity): Float + abstract suspend fun getHighestComparisonScoreForCandidate(candidate: CandidateRecord): Float } diff --git a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/FaceBioSDK.kt b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/FaceBioSDK.kt index a11b2bc478..87fc56fd22 100644 --- a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/FaceBioSDK.kt +++ b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/FaceBioSDK.kt @@ -1,6 +1,6 @@ package com.simprints.face.infra.biosdkresolver -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.face.infra.basebiosdk.detection.FaceDetector import com.simprints.face.infra.basebiosdk.initialization.FaceBioSdkInitializer import com.simprints.face.infra.basebiosdk.matching.FaceMatcher @@ -15,5 +15,5 @@ interface FaceBioSDK { fun matcherName(): String - fun createMatcher(probeSamples: List): FaceMatcher + fun createMatcher(probeReference: BiometricReferenceCapture): FaceMatcher } diff --git a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdk.kt b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdk.kt index 4b5de11830..0111264c60 100644 --- a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdk.kt +++ b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdk.kt @@ -1,6 +1,6 @@ package com.simprints.face.infra.biosdkresolver -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.face.infra.basebiosdk.matching.FaceMatcher import com.simprints.face.infra.rocv1.detection.RocV1Detector import com.simprints.face.infra.rocv1.detection.RocV1Detector.Companion.RANK_ONE_TEMPLATE_FORMAT_1_23 @@ -20,5 +20,5 @@ class RocV1BioSdk @Inject constructor( override fun matcherName(): String = "RANK_ONE" - override fun createMatcher(probeSamples: List): FaceMatcher = RocV1Matcher(probeSamples) + override fun createMatcher(probeReference: BiometricReferenceCapture): FaceMatcher = RocV1Matcher(probeReference) } diff --git a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdk.kt b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdk.kt index f3b000b023..f988a8fdff 100644 --- a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdk.kt +++ b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdk.kt @@ -1,6 +1,6 @@ package com.simprints.face.infra.biosdkresolver -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.face.infra.basebiosdk.matching.FaceMatcher import com.simprints.face.infra.rocv3.detection.RocV3Detector import com.simprints.face.infra.rocv3.detection.RocV3Detector.Companion.RANK_ONE_TEMPLATE_FORMAT_3_1 @@ -20,5 +20,5 @@ class RocV3BioSdk @Inject constructor( override fun matcherName(): String = "RANK_ONE" - override fun createMatcher(probeSamples: List): FaceMatcher = RocV3Matcher(probeSamples) + override fun createMatcher(probeReference: BiometricReferenceCapture): FaceMatcher = RocV3Matcher(probeReference) } diff --git a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/SimFaceBioSdk.kt b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/SimFaceBioSdk.kt index fe4be76ae8..c5f8527c5f 100644 --- a/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/SimFaceBioSdk.kt +++ b/face/infra/bio-sdk-resolver/src/main/java/com/simprints/face/infra/biosdkresolver/SimFaceBioSdk.kt @@ -1,7 +1,7 @@ package com.simprints.face.infra.biosdkresolver import com.simprints.biometrics.simface.SimFace -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.face.infra.basebiosdk.matching.FaceMatcher import com.simprints.face.infra.simface.detection.SimFaceDetector import com.simprints.face.infra.simface.initialization.SimFaceInitializer @@ -21,5 +21,5 @@ class SimFaceBioSdk @Inject constructor( override fun matcherName(): String = "SIM_FACE" - override fun createMatcher(probeSamples: List): FaceMatcher = SimFaceMatcher(simFace, probeSamples) + override fun createMatcher(probeReference: BiometricReferenceCapture): FaceMatcher = SimFaceMatcher(simFace, probeReference) } diff --git a/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdkTest.kt b/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdkTest.kt index 8a42d5549f..4bdff76da4 100644 --- a/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdkTest.kt +++ b/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV1BioSdkTest.kt @@ -1,22 +1,20 @@ package com.simprints.face.infra.biosdkresolver - import com.google.common.truth.Truth.* import io.mockk.* import org.junit.Test class RocV1BioSdkTest { - - private lateinit var rocV1BioSdk: RocV1BioSdk @Test fun createMatcher() { rocV1BioSdk = RocV1BioSdk(mockk(), mockk()) - val matcher = rocV1BioSdk.createMatcher(emptyList()) + val matcher = rocV1BioSdk.createMatcher( + mockk { every { templates } returns emptyList() }, + ) assertThat(matcher).isNotNull() } - } diff --git a/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdkTest.kt b/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdkTest.kt index 43fdc8d41c..a93a4b3149 100644 --- a/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdkTest.kt +++ b/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/RocV3BioSdkTest.kt @@ -11,7 +11,9 @@ class RocV3BioSdkTest { fun createMatcher() { rocV3BioSdk = RocV3BioSdk(mockk(), mockk()) - val matcher = rocV3BioSdk.createMatcher(emptyList()) + val matcher = rocV3BioSdk.createMatcher( + mockk { every { templates } returns emptyList() }, + ) assertThat(matcher).isNotNull() } diff --git a/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/SimFaceSdkTest.kt b/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/SimFaceSdkTest.kt index 2c4cb3022f..fdb954bcdb 100644 --- a/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/SimFaceSdkTest.kt +++ b/face/infra/bio-sdk-resolver/src/test/java/com/simprints/face/infra/biosdkresolver/SimFaceSdkTest.kt @@ -18,7 +18,7 @@ class SimFaceSdkTest { simFace = mockk(relaxed = true), ) - val matcher = bioSdk.createMatcher(emptyList()) + val matcher = bioSdk.createMatcher(mockk()) Truth.assertThat(matcher).isNotNull() } diff --git a/face/infra/roc-v1/src/main/java/com/simprints/face/infra/rocv1/matching/RocV1Matcher.kt b/face/infra/roc-v1/src/main/java/com/simprints/face/infra/rocv1/matching/RocV1Matcher.kt index 5979018813..b9b596b72a 100644 --- a/face/infra/roc-v1/src/main/java/com/simprints/face/infra/rocv1/matching/RocV1Matcher.kt +++ b/face/infra/roc-v1/src/main/java/com/simprints/face/infra/rocv1/matching/RocV1Matcher.kt @@ -1,8 +1,8 @@ package com.simprints.face.infra.rocv1.matching import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.face.infra.basebiosdk.matching.FaceMatcher import io.rankone.rocsdk.embedded.SWIGTYPE_p_unsigned_char import io.rankone.rocsdk.embedded.roc @@ -12,18 +12,19 @@ import io.rankone.rocsdk.embedded.rocConstants.ROC_FAST_FV_SIZE reason = "This function uses roc class that has native functions and can't be mocked", ) class RocV1Matcher( - override val probeSamples: List, -) : FaceMatcher(probeSamples) { - var probeTemplates: List = probeSamples.mapIndexed { i, probe -> - val probeTemplate: SWIGTYPE_p_unsigned_char = - roc.new_uint8_t_array(ROC_FAST_FV_SIZE.toInt()) - roc.memmove(roc.roc_cast(probeTemplate), probe.template) - probeTemplate - } + override val probeReference: BiometricReferenceCapture, +) : FaceMatcher(probeReference) { + var nativeProbeTemplates: List = probeReference.templates + .map { it.template } + .map { probe -> + val probeTemplate: SWIGTYPE_p_unsigned_char = roc.new_uint8_t_array(ROC_FAST_FV_SIZE.toInt()) + roc.memmove(roc.roc_cast(probeTemplate), probe) + probeTemplate + } - override suspend fun getHighestComparisonScoreForCandidate(candidate: Identity): Float = probeTemplates + override suspend fun getHighestComparisonScoreForCandidate(candidate: CandidateRecord): Float = nativeProbeTemplates .flatMap { probeTemplate -> - candidate.samples.map { face -> + candidate.references.flatMap { it.templates }.map { face -> getSimilarityScoreForCandidate(probeTemplate, face.template) } }.max() @@ -47,6 +48,6 @@ class RocV1Matcher( } override fun close() { - probeTemplates.forEach { roc.delete_uint8_t_array(it) } + nativeProbeTemplates.forEach { roc.delete_uint8_t_array(it) } } } diff --git a/face/infra/roc-v1/src/test/java/com/simprints/infra/rocwrapper/matching/RocV1MatcherTest.kt b/face/infra/roc-v1/src/test/java/com/simprints/infra/rocwrapper/matching/RocV1MatcherTest.kt index 4fe85a6425..8e2665e016 100644 --- a/face/infra/roc-v1/src/test/java/com/simprints/infra/rocwrapper/matching/RocV1MatcherTest.kt +++ b/face/infra/roc-v1/src/test/java/com/simprints/infra/rocwrapper/matching/RocV1MatcherTest.kt @@ -2,12 +2,14 @@ package com.simprints.infra.rocwrapper.matching import com.google.common.truth.* import com.simprints.face.infra.rocv1.matching.RocV1Matcher +import io.mockk.every +import io.mockk.mockk import org.junit.Test class RocV1MatcherTest { // Dummy test to generate jacoco reports. @Test fun getMatcherName() { - Truth.assertThat(RocV1Matcher(emptyList())).isNotNull() + Truth.assertThat(RocV1Matcher(mockk { every { templates } returns emptyList() })).isNotNull() } } diff --git a/face/infra/roc-v3/src/main/java/com/simprints/face/infra/rocv3/matching/RocV3Matcher.kt b/face/infra/roc-v3/src/main/java/com/simprints/face/infra/rocv3/matching/RocV3Matcher.kt index 9bdbaec3b6..4bc5641ce7 100644 --- a/face/infra/roc-v3/src/main/java/com/simprints/face/infra/rocv3/matching/RocV3Matcher.kt +++ b/face/infra/roc-v3/src/main/java/com/simprints/face/infra/rocv3/matching/RocV3Matcher.kt @@ -4,26 +4,27 @@ import ai.roc.rocsdk.embedded.SWIGTYPE_p_unsigned_char import ai.roc.rocsdk.embedded.roc import ai.roc.rocsdk.embedded.rocConstants.ROC_FACE_FAST_FV_SIZE import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.face.infra.basebiosdk.matching.FaceMatcher @ExcludedFromGeneratedTestCoverageReports( reason = "This function uses roc class that has native functions and can't be mocked", ) class RocV3Matcher( - override val probeSamples: List, -) : FaceMatcher(probeSamples) { - var probeTemplates: List = probeSamples.mapIndexed { i, probe -> - val probeTemplate: SWIGTYPE_p_unsigned_char = - roc.new_uint8_t_array(ROC_FACE_FAST_FV_SIZE.toInt()) - roc.memmove(roc.roc_cast(probeTemplate), probe.template) - probeTemplate - } + override val probeReference: BiometricReferenceCapture, +) : FaceMatcher(probeReference) { + var nativeProbeTemplates: List = probeReference.templates + .map { it.template } + .map { probe -> + val probeTemplate: SWIGTYPE_p_unsigned_char = roc.new_uint8_t_array(ROC_FACE_FAST_FV_SIZE.toInt()) + roc.memmove(roc.roc_cast(probeTemplate), probe) + probeTemplate + } - override suspend fun getHighestComparisonScoreForCandidate(candidate: Identity): Float = probeTemplates + override suspend fun getHighestComparisonScoreForCandidate(candidate: CandidateRecord): Float = nativeProbeTemplates .flatMap { probeTemplate -> - candidate.samples.map { face -> + candidate.references.flatMap { it.templates }.map { face -> getSimilarityScoreForCandidate(probeTemplate, face.template) } }.max() @@ -47,6 +48,6 @@ class RocV3Matcher( } override fun close() { - probeTemplates.forEach { roc.delete_uint8_t_array(it) } + nativeProbeTemplates.forEach { roc.delete_uint8_t_array(it) } } } diff --git a/face/infra/roc-v3/src/test/java/com/simprints/infra/rocwrapper/matching/RocV3MatcherTest.kt b/face/infra/roc-v3/src/test/java/com/simprints/infra/rocwrapper/matching/RocV3MatcherTest.kt index bc2424efb2..652013f10e 100644 --- a/face/infra/roc-v3/src/test/java/com/simprints/infra/rocwrapper/matching/RocV3MatcherTest.kt +++ b/face/infra/roc-v3/src/test/java/com/simprints/infra/rocwrapper/matching/RocV3MatcherTest.kt @@ -2,12 +2,14 @@ package com.simprints.infra.rocwrapper.matching import com.google.common.truth.Truth import com.simprints.face.infra.rocv3.matching.RocV3Matcher +import io.mockk.every +import io.mockk.mockk import org.junit.Test // Dummy test to generate jacoco reports. class RocV3MatcherTest { @Test fun getMatcherName() { - Truth.assertThat(RocV3Matcher(emptyList())).isNotNull() + Truth.assertThat(RocV3Matcher(mockk { every { templates } returns emptyList() })).isNotNull() } } diff --git a/face/infra/simface/src/main/java/com/simprints/face/infra/simface/matching/SimFaceMatcher.kt b/face/infra/simface/src/main/java/com/simprints/face/infra/simface/matching/SimFaceMatcher.kt index 8e7a4db345..22542ae55a 100644 --- a/face/infra/simface/src/main/java/com/simprints/face/infra/simface/matching/SimFaceMatcher.kt +++ b/face/infra/simface/src/main/java/com/simprints/face/infra/simface/matching/SimFaceMatcher.kt @@ -1,19 +1,19 @@ package com.simprints.face.infra.simface.matching import com.simprints.biometrics.simface.SimFace -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.face.infra.basebiosdk.matching.FaceMatcher class SimFaceMatcher( private val simFace: SimFace, - override val probeSamples: List, -) : FaceMatcher(probeSamples) { - private val probeTemplates = probeSamples.map { it.template } - - override suspend fun getHighestComparisonScoreForCandidate(candidate: Identity): Float = probeTemplates + override val probeReference: BiometricReferenceCapture, +) : FaceMatcher(probeReference) { + override suspend fun getHighestComparisonScoreForCandidate(candidate: CandidateRecord): Float = probeReference + .templates + .map { it.template } .flatMap { probeTemplate -> - candidate.samples.map { face -> + candidate.references.flatMap { it.templates }.map { face -> val baseScore = simFace.verificationScore(probeTemplate, face.template) // TODO: remove the adjustment after we find out why the returned range is biased towards [0.5;1] (baseScore - 0.5).coerceAtLeast(0.0).toFloat() * 200f diff --git a/face/infra/simface/src/test/java/com/simprints/face/infra/simface/matching/SimFaceMatcherTest.kt b/face/infra/simface/src/test/java/com/simprints/face/infra/simface/matching/SimFaceMatcherTest.kt index 22409cce7c..0c0d8ba4e4 100644 --- a/face/infra/simface/src/test/java/com/simprints/face/infra/simface/matching/SimFaceMatcherTest.kt +++ b/face/infra/simface/src/test/java/com/simprints/face/infra/simface/matching/SimFaceMatcherTest.kt @@ -3,9 +3,10 @@ package com.simprints.face.infra.simface.matching import com.google.common.truth.Truth.* import com.simprints.biometrics.simface.SimFace import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.reference.CandidateRecord import io.mockk.* import kotlinx.coroutines.test.runTest import org.junit.Test @@ -14,7 +15,7 @@ import org.junit.Test class SimFaceMatcherTest { @Test fun getMatcherName() { - assertThat(SimFaceMatcher(mockk(relaxed = true), emptyList())).isNotNull() + assertThat(SimFaceMatcher(mockk(relaxed = true), mockk { })).isNotNull() } @Test @@ -44,23 +45,27 @@ class SimFaceMatcherTest { } val matcher = SimFaceMatcher( simFace = simFace, - probeSamples = listOf( - CaptureSample( - captureEventId = "id", - modality = Modality.FACE, - format = "ROC", - template = byteArrayOf(1), - ), - ), + probeReference = mockk { + every { templates } returns listOf( + BiometricTemplateCapture( + captureEventId = "captureId", + template = byteArrayOf(1), + ), + ) + }, ) val result = matcher.getHighestComparisonScoreForCandidate( - candidate = Identity( + candidate = CandidateRecord( subjectId = "id", - samples = listOf( - Sample( + references = listOf( + BiometricReference( referenceId = "id", modality = Modality.FACE, - template = byteArrayOf(1), + templates = listOf( + BiometricTemplate( + template = byteArrayOf(1), + ), + ), format = "ROC", ), ), @@ -77,23 +82,27 @@ class SimFaceMatcherTest { } val matcher = SimFaceMatcher( simFace = simFace, - probeSamples = listOf( - CaptureSample( - captureEventId = "id", - modality = Modality.FACE, - format = "ROC", - template = byteArrayOf(1), - ), - ), + probeReference = mockk { + every { templates } returns listOf( + BiometricTemplateCapture( + captureEventId = "captureId", + template = byteArrayOf(1), + ), + ) + }, ) val result = matcher.getHighestComparisonScoreForCandidate( - candidate = Identity( + candidate = CandidateRecord( subjectId = "id", - samples = listOf( - Sample( + references = listOf( + BiometricReference( referenceId = "id", modality = Modality.FACE, - template = byteArrayOf(1), + templates = listOf( + BiometricTemplate( + template = byteArrayOf(1), + ), + ), format = "ROC", ), ), diff --git a/feature/client-api/src/main/java/com/simprints/feature/clientapi/ClientApiViewModel.kt b/feature/client-api/src/main/java/com/simprints/feature/clientapi/ClientApiViewModel.kt index ab17f3af27..09964b602c 100644 --- a/feature/client-api/src/main/java/com/simprints/feature/clientapi/ClientApiViewModel.kt +++ b/feature/client-api/src/main/java/com/simprints/feature/clientapi/ClientApiViewModel.kt @@ -19,7 +19,7 @@ import com.simprints.feature.clientapi.models.ClientApiError import com.simprints.feature.clientapi.usecases.CreateSessionIfRequiredUseCase import com.simprints.feature.clientapi.usecases.DeleteSessionEventsIfNeededUseCase import com.simprints.feature.clientapi.usecases.GetCurrentSessionIdUseCase -import com.simprints.feature.clientapi.usecases.GetEnrolmentCreationEventForSubjectUseCase +import com.simprints.feature.clientapi.usecases.GetEnrolmentCreationEventForRecordUseCase import com.simprints.feature.clientapi.usecases.IsFlowCompletedWithErrorUseCase import com.simprints.feature.clientapi.usecases.SimpleEventReporter import com.simprints.infra.config.store.models.Project @@ -51,7 +51,7 @@ class ClientApiViewModel @Inject internal constructor( private val simpleEventReporter: SimpleEventReporter, private val getCurrentSessionId: GetCurrentSessionIdUseCase, private val createSessionIfRequiredUseCase: CreateSessionIfRequiredUseCase, - private val getEnrolmentCreationEventForSubject: GetEnrolmentCreationEventForSubjectUseCase, + private val getEnrolmentCreationEventForRecord: GetEnrolmentCreationEventForRecordUseCase, private val deleteSessionEventsIfNeeded: DeleteSessionEventsIfNeededUseCase, private val isFlowCompletedWithError: IsFlowCompletedWithErrorUseCase, private val configManager: ConfigManager, @@ -107,7 +107,7 @@ class ClientApiViewModel @Inject internal constructor( simpleEventReporter.closeCurrentSessionNormally() val coSyncEnrolmentRecords = - getEnrolmentCreationEventForSubject(action.projectId, enrolResponse.guid) + getEnrolmentCreationEventForRecord(action.projectId, enrolResponse.guid) deleteSessionEventsIfNeeded(currentSessionId) diff --git a/feature/client-api/src/main/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForSubjectUseCase.kt b/feature/client-api/src/main/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForRecordUseCase.kt similarity index 88% rename from feature/client-api/src/main/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForSubjectUseCase.kt rename to feature/client-api/src/main/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForRecordUseCase.kt index f0dc8fad66..1f4f8b602f 100644 --- a/feature/client-api/src/main/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForSubjectUseCase.kt +++ b/feature/client-api/src/main/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForRecordUseCase.kt @@ -10,14 +10,14 @@ import com.simprints.infra.config.store.models.canCoSyncAllData import com.simprints.infra.config.store.models.canCoSyncBiometricData import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.event.cosync.CoSyncEnrolmentRecordEvents import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordCreationEvent import com.simprints.infra.logging.Simber import javax.inject.Inject -internal class GetEnrolmentCreationEventForSubjectUseCase @Inject constructor( +internal class GetEnrolmentCreationEventForRecordUseCase @Inject constructor( private val configManager: ConfigManager, private val enrolmentRecordRepository: EnrolmentRecordRepository, private val encoder: EncodingUtils, @@ -34,7 +34,7 @@ internal class GetEnrolmentCreationEventForSubjectUseCase @Inject constructor( } val recordCreationEvent = enrolmentRecordRepository - .load(SubjectQuery(projectId = projectId, subjectId = subjectId)) + .load(EnrolmentRecordQuery(projectId = projectId, subjectId = subjectId)) .firstOrNull() ?.fromSubjectToEnrolmentCreationEvent() @@ -49,12 +49,12 @@ internal class GetEnrolmentCreationEventForSubjectUseCase @Inject constructor( return jsonHelper.toJson(CoSyncEnrolmentRecordEvents(listOf(recordCreationEvent)), coSyncSerializationModule) } - private fun Subject.fromSubjectToEnrolmentCreationEvent() = EnrolmentRecordCreationEvent( + private fun EnrolmentRecord.fromSubjectToEnrolmentCreationEvent() = EnrolmentRecordCreationEvent( subjectId = subjectId, projectId = projectId, moduleId = moduleId, attendantId = attendantId, - biometricReferences = EnrolmentRecordCreationEvent.buildBiometricReferences(samples, encoder), + biometricReferences = EnrolmentRecordCreationEvent.buildBiometricReferences(references, encoder), externalCredentials = externalCredentials, ) diff --git a/feature/client-api/src/test/java/com/simprints/feature/clientapi/ClientApiViewModelTest.kt b/feature/client-api/src/test/java/com/simprints/feature/clientapi/ClientApiViewModelTest.kt index fb26468565..6cb3c2cec7 100644 --- a/feature/client-api/src/test/java/com/simprints/feature/clientapi/ClientApiViewModelTest.kt +++ b/feature/client-api/src/test/java/com/simprints/feature/clientapi/ClientApiViewModelTest.kt @@ -16,7 +16,7 @@ import com.simprints.feature.clientapi.mappers.response.ActionToIntentMapper import com.simprints.feature.clientapi.usecases.CreateSessionIfRequiredUseCase import com.simprints.feature.clientapi.usecases.DeleteSessionEventsIfNeededUseCase import com.simprints.feature.clientapi.usecases.GetCurrentSessionIdUseCase -import com.simprints.feature.clientapi.usecases.GetEnrolmentCreationEventForSubjectUseCase +import com.simprints.feature.clientapi.usecases.GetEnrolmentCreationEventForRecordUseCase import com.simprints.feature.clientapi.usecases.IsFlowCompletedWithErrorUseCase import com.simprints.feature.clientapi.usecases.SimpleEventReporter import com.simprints.infra.config.store.models.Project @@ -61,7 +61,7 @@ internal class ClientApiViewModelTest { lateinit var createSessionIfRequiredUseCase: CreateSessionIfRequiredUseCase @MockK - lateinit var getEnrolmentCreationEventForSubject: GetEnrolmentCreationEventForSubjectUseCase + lateinit var getEnrolmentCreationEventForRecord: GetEnrolmentCreationEventForRecordUseCase @MockK lateinit var deleteSessionEventsIfNeeded: DeleteSessionEventsIfNeededUseCase @@ -88,7 +88,7 @@ internal class ClientApiViewModelTest { MockKAnnotations.init(this, relaxUnitFun = true) coEvery { getCurrentSessionId.invoke() } returns "sessionId" - coEvery { getEnrolmentCreationEventForSubject.invoke(any(), any()) } returns "recordsJson" + coEvery { getEnrolmentCreationEventForRecord.invoke(any(), any()) } returns "recordsJson" every { resultMapper.invoke(any()) } returns mockk() every { isFlowCompletedWithError.invoke(any()) } returns false coEvery { deleteSessionEventsIfNeeded.invoke(any()) } returns mockk() @@ -101,7 +101,7 @@ internal class ClientApiViewModelTest { simpleEventReporter = simpleEventReporter, getCurrentSessionId = getCurrentSessionId, createSessionIfRequiredUseCase = createSessionIfRequiredUseCase, - getEnrolmentCreationEventForSubject = getEnrolmentCreationEventForSubject, + getEnrolmentCreationEventForRecord = getEnrolmentCreationEventForRecord, deleteSessionEventsIfNeeded = deleteSessionEventsIfNeeded, isFlowCompletedWithError = isFlowCompletedWithError, configManager = configManager, diff --git a/feature/client-api/src/test/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForSubjectUseCaseTest.kt b/feature/client-api/src/test/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForRecordUseCaseTest.kt similarity index 94% rename from feature/client-api/src/test/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForSubjectUseCaseTest.kt rename to feature/client-api/src/test/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForRecordUseCaseTest.kt index 992cf81fa2..e12b2bb34f 100644 --- a/feature/client-api/src/test/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForSubjectUseCaseTest.kt +++ b/feature/client-api/src/test/java/com/simprints/feature/clientapi/usecases/GetEnrolmentCreationEventForRecordUseCaseTest.kt @@ -18,7 +18,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test -class GetEnrolmentCreationEventForSubjectUseCaseTest { +class GetEnrolmentCreationEventForRecordUseCaseTest { @get:Rule val testCoroutineRule = TestCoroutineRule() @@ -34,7 +34,7 @@ class GetEnrolmentCreationEventForSubjectUseCaseTest { @MockK private lateinit var jsonHelper: JsonHelper - private lateinit var useCase: GetEnrolmentCreationEventForSubjectUseCase + private lateinit var useCase: GetEnrolmentCreationEventForRecordUseCase @Before fun setUp() { @@ -42,7 +42,7 @@ class GetEnrolmentCreationEventForSubjectUseCaseTest { every { jsonHelper.toJson(any()) } returns "json" - useCase = GetEnrolmentCreationEventForSubjectUseCase( + useCase = GetEnrolmentCreationEventForRecordUseCase( configManager, enrolmentRecordRepository, encoder, diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionItemAdapter.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionItemAdapter.kt index b06511e5b0..2bed086f42 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionItemAdapter.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionItemAdapter.kt @@ -4,7 +4,7 @@ import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.feature.dashboard.databinding.HeaderSdkNameBinding import com.simprints.feature.dashboard.databinding.ItemFingerSelectionBinding import com.simprints.infra.resources.R as IDR @@ -89,18 +89,18 @@ internal class FingerSelectionItemAdapter( } } -fun SampleIdentifier.toString(context: Context) = context.getString( +fun TemplateIdentifier.toString(context: Context) = context.getString( when (this) { - SampleIdentifier.LEFT_THUMB -> IDR.string.fingerprint_capture_finger_l_1 - SampleIdentifier.LEFT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_l_2 - SampleIdentifier.LEFT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_l_3 - SampleIdentifier.LEFT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_l_4 - SampleIdentifier.LEFT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_l_5 - SampleIdentifier.RIGHT_THUMB -> IDR.string.fingerprint_capture_finger_r_1 - SampleIdentifier.RIGHT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_r_2 - SampleIdentifier.RIGHT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_r_3 - SampleIdentifier.RIGHT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_r_4 - SampleIdentifier.RIGHT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_r_5 - SampleIdentifier.NONE -> throw IllegalArgumentException("Incorrect sample identifier") + TemplateIdentifier.LEFT_THUMB -> IDR.string.fingerprint_capture_finger_l_1 + TemplateIdentifier.LEFT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_l_2 + TemplateIdentifier.LEFT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_l_3 + TemplateIdentifier.LEFT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_l_4 + TemplateIdentifier.LEFT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_l_5 + TemplateIdentifier.RIGHT_THUMB -> IDR.string.fingerprint_capture_finger_r_1 + TemplateIdentifier.RIGHT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_r_2 + TemplateIdentifier.RIGHT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_r_3 + TemplateIdentifier.RIGHT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_r_4 + TemplateIdentifier.RIGHT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_r_5 + TemplateIdentifier.NONE -> throw IllegalArgumentException("Incorrect sample identifier") }, ) diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModel.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModel.kt index 9c643942f5..75aeef8bcd 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModel.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModel.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.sync.ConfigManager import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -31,7 +31,7 @@ internal class FingerSelectionViewModel @Inject constructor( } } - private fun List.toFingerSelectionItems(): List { + private fun List.toFingerSelectionItems(): List { val result = mutableListOf() this.forEach { finger -> val alreadyExistingFingerSelection = result.firstOrNull { fingerSelectionItem -> @@ -54,6 +54,6 @@ data class FingerSelectionSection( ) data class FingerSelectionItem( - var finger: SampleIdentifier, + var finger: TemplateIdentifier, var quantity: Int, ) diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/moduleselection/repository/ModuleRepositoryImpl.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/moduleselection/repository/ModuleRepositoryImpl.kt index 026345c5f8..91c193211b 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/moduleselection/repository/ModuleRepositoryImpl.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/moduleselection/repository/ModuleRepositoryImpl.kt @@ -3,7 +3,7 @@ package com.simprints.feature.dashboard.settings.syncinfo.moduleselection.reposi import com.simprints.core.domain.tokenization.values import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.eventsync.EventSyncManager import com.simprints.infra.logging.LoggingConstants.CrashReportTag.SETTINGS import com.simprints.infra.logging.LoggingConstants.CrashReportingCustomKeys.MODULE_IDS @@ -18,7 +18,8 @@ internal class ModuleRepositoryImpl @Inject constructor( ) : ModuleRepository { override suspend fun getModules(): List = configManager .getProjectConfiguration() - .synchronization.down.simprints?.moduleOptions + .synchronization.down.simprints + ?.moduleOptions ?.map { Module(it, isModuleSelected(it.value)) } ?: emptyList() @@ -30,7 +31,8 @@ internal class ModuleRepositoryImpl @Inject constructor( override suspend fun getMaxNumberOfModules(): Int = configManager .getProjectConfiguration() - .synchronization.down.simprints?.maxNbOfModules ?: 0 + .synchronization.down.simprints + ?.maxNbOfModules ?: 0 private suspend fun isModuleSelected(moduleName: String): Boolean = configManager .getDeviceConfiguration() @@ -50,7 +52,7 @@ internal class ModuleRepositoryImpl @Inject constructor( private suspend fun handleUnselectedModules(unselectedModules: List) { val queries = unselectedModules.map { - SubjectQuery(moduleId = it.name) + EnrolmentRecordQuery(moduleId = it.name) } enrolmentRecordRepository.delete(queries) diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveConfigurationChangesUseCase.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveConfigurationChangesUseCase.kt index 0d1d72ed74..2b70846900 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveConfigurationChangesUseCase.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveConfigurationChangesUseCase.kt @@ -8,7 +8,7 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import kotlinx.coroutines.flow.combine import javax.inject.Inject @@ -37,7 +37,7 @@ internal class ObserveConfigurationChangesUseCase @Inject constructor( ) }.value, count = enrolmentRecordRepository.count( - SubjectQuery(projectId = project.id, moduleId = moduleName), + EnrolmentRecordQuery(projectId = project.id, moduleId = moduleName), ), ) } diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt index dbe17e054c..3f8b5b8263 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt @@ -22,7 +22,7 @@ import com.simprints.infra.config.store.models.isModuleSelectionAvailable import com.simprints.infra.config.store.models.isSampleUploadEnabledInProject import com.simprints.infra.config.store.models.isSimprintsEventDownSyncAllowed import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.event.domain.models.EventType import com.simprints.infra.eventsync.EventSyncManager import com.simprints.infra.eventsync.permission.CommCarePermissionChecker @@ -193,7 +193,7 @@ internal class ObserveSyncInfoUseCase @Inject constructor( isEventSyncInProgress || projectId.isBlank() -> null // without project ID, repository access attempts will throw an exception - else -> enrolmentRecordRepository.count(SubjectQuery(projectId)) + else -> enrolmentRecordRepository.count(EnrolmentRecordQuery(projectId)) } val recordsToUpload = when { isEventSyncInProgress -> null diff --git a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionFragmentTest.kt b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionFragmentTest.kt index 8c16f1aa92..bad8bdff4f 100644 --- a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionFragmentTest.kt +++ b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionFragmentTest.kt @@ -6,7 +6,7 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.feature.dashboard.R import com.simprints.testtools.hilt.launchFragmentInHiltContainer import dagger.hilt.android.testing.BindValue @@ -43,9 +43,9 @@ class FingerSelectionFragmentTest { FingerSelectionSection( sdkName = SIM_MATCHER_NAME, items = listOf( - FingerSelectionItem(SampleIdentifier.LEFT_THUMB, 1), - FingerSelectionItem(SampleIdentifier.RIGHT_THUMB, 2), - FingerSelectionItem(SampleIdentifier.LEFT_INDEX_FINGER, 3), + FingerSelectionItem(TemplateIdentifier.LEFT_THUMB, 1), + FingerSelectionItem(TemplateIdentifier.RIGHT_THUMB, 2), + FingerSelectionItem(TemplateIdentifier.LEFT_INDEX_FINGER, 3), ), ), ), diff --git a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModelTest.kt b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModelTest.kt index 8afc217e58..29c7eb15aa 100644 --- a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModelTest.kt +++ b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/fingerselection/FingerSelectionViewModelTest.kt @@ -2,7 +2,7 @@ package com.simprints.feature.dashboard.settings.fingerselection import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.sync.ConfigManager import com.simprints.testtools.common.coroutines.TestCoroutineRule @@ -26,10 +26,10 @@ class FingerSelectionViewModelTest { @Test fun start_loadsSingleSdkFingerStatesCorrectly() { every { fingerprintConfiguration.secugenSimMatcher?.fingersToCapture } returns listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_THUMB, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_THUMB, ) every { fingerprintConfiguration.nec } returns null @@ -42,8 +42,8 @@ class FingerSelectionViewModelTest { assertThat(fingerSelections?.first()?.items) .containsExactlyElementsIn( listOf( - FingerSelectionItem(SampleIdentifier.LEFT_THUMB, 2), - FingerSelectionItem(SampleIdentifier.RIGHT_THUMB, 2), + FingerSelectionItem(TemplateIdentifier.LEFT_THUMB, 2), + FingerSelectionItem(TemplateIdentifier.RIGHT_THUMB, 2), ), ).inOrder() } @@ -51,18 +51,18 @@ class FingerSelectionViewModelTest { @Test fun start_loadsTwoSdksFingerStatesCorrectly() { every { fingerprintConfiguration.secugenSimMatcher?.fingersToCapture } returns listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_THUMB, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_THUMB, ) every { fingerprintConfiguration.nec?.fingersToCapture } returns listOf( - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, ) viewModel.start() @@ -74,8 +74,8 @@ class FingerSelectionViewModelTest { assertThat(fingerSelections?.first()?.items) .containsExactlyElementsIn( listOf( - FingerSelectionItem(SampleIdentifier.LEFT_THUMB, 1), - FingerSelectionItem(SampleIdentifier.RIGHT_THUMB, 2), + FingerSelectionItem(TemplateIdentifier.LEFT_THUMB, 1), + FingerSelectionItem(TemplateIdentifier.RIGHT_THUMB, 2), ), ).inOrder() assertThat(fingerSelections?.get(1)?.sdkName).isEqualTo("NEC") @@ -83,8 +83,8 @@ class FingerSelectionViewModelTest { assertThat(fingerSelections?.get(1)?.items) .containsExactlyElementsIn( listOf( - FingerSelectionItem(SampleIdentifier.LEFT_INDEX_FINGER, 3), - FingerSelectionItem(SampleIdentifier.RIGHT_INDEX_FINGER, 4), + FingerSelectionItem(TemplateIdentifier.LEFT_INDEX_FINGER, 3), + FingerSelectionItem(TemplateIdentifier.RIGHT_INDEX_FINGER, 4), ), ).inOrder() } @@ -92,36 +92,36 @@ class FingerSelectionViewModelTest { @Test fun scatteredFingers_areAggregated() { every { fingerprintConfiguration.secugenSimMatcher?.fingersToCapture } returns listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_INDEX_FINGER, - SampleIdentifier.LEFT_3RD_FINGER, - SampleIdentifier.LEFT_3RD_FINGER, - SampleIdentifier.LEFT_4TH_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.LEFT_5TH_FINGER, - SampleIdentifier.LEFT_3RD_FINGER, - SampleIdentifier.LEFT_4TH_FINGER, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.LEFT_5TH_FINGER, - SampleIdentifier.RIGHT_4TH_FINGER, - SampleIdentifier.LEFT_4TH_FINGER, - SampleIdentifier.RIGHT_3RD_FINGER, - SampleIdentifier.RIGHT_4TH_FINGER, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.LEFT_5TH_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, - SampleIdentifier.RIGHT_4TH_FINGER, - SampleIdentifier.LEFT_4TH_FINGER, - SampleIdentifier.LEFT_5TH_FINGER, - SampleIdentifier.RIGHT_3RD_FINGER, - SampleIdentifier.LEFT_5TH_FINGER, - SampleIdentifier.RIGHT_4TH_FINGER, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_3RD_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_4TH_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_5TH_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_4TH_FINGER, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_5TH_FINGER, + TemplateIdentifier.RIGHT_4TH_FINGER, + TemplateIdentifier.LEFT_4TH_FINGER, + TemplateIdentifier.RIGHT_3RD_FINGER, + TemplateIdentifier.RIGHT_4TH_FINGER, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_5TH_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.RIGHT_4TH_FINGER, + TemplateIdentifier.LEFT_4TH_FINGER, + TemplateIdentifier.LEFT_5TH_FINGER, + TemplateIdentifier.RIGHT_3RD_FINGER, + TemplateIdentifier.LEFT_5TH_FINGER, + TemplateIdentifier.RIGHT_4TH_FINGER, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_3RD_FINGER, ) every { fingerprintConfiguration.nec } returns null @@ -134,16 +134,16 @@ class FingerSelectionViewModelTest { assertThat(fingerSelections?.first()?.items) .containsExactlyElementsIn( listOf( - FingerSelectionItem(SampleIdentifier.LEFT_THUMB, 1), - FingerSelectionItem(SampleIdentifier.RIGHT_THUMB, 1), - FingerSelectionItem(SampleIdentifier.RIGHT_INDEX_FINGER, 2), - FingerSelectionItem(SampleIdentifier.LEFT_3RD_FINGER, 3), - FingerSelectionItem(SampleIdentifier.LEFT_4TH_FINGER, 4), - FingerSelectionItem(SampleIdentifier.LEFT_INDEX_FINGER, 2), - FingerSelectionItem(SampleIdentifier.RIGHT_5TH_FINGER, 5), - FingerSelectionItem(SampleIdentifier.LEFT_5TH_FINGER, 5), - FingerSelectionItem(SampleIdentifier.RIGHT_4TH_FINGER, 4), - FingerSelectionItem(SampleIdentifier.RIGHT_3RD_FINGER, 3), + FingerSelectionItem(TemplateIdentifier.LEFT_THUMB, 1), + FingerSelectionItem(TemplateIdentifier.RIGHT_THUMB, 1), + FingerSelectionItem(TemplateIdentifier.RIGHT_INDEX_FINGER, 2), + FingerSelectionItem(TemplateIdentifier.LEFT_3RD_FINGER, 3), + FingerSelectionItem(TemplateIdentifier.LEFT_4TH_FINGER, 4), + FingerSelectionItem(TemplateIdentifier.LEFT_INDEX_FINGER, 2), + FingerSelectionItem(TemplateIdentifier.RIGHT_5TH_FINGER, 5), + FingerSelectionItem(TemplateIdentifier.LEFT_5TH_FINGER, 5), + FingerSelectionItem(TemplateIdentifier.RIGHT_4TH_FINGER, 4), + FingerSelectionItem(TemplateIdentifier.RIGHT_3RD_FINGER, 3), ), ).inOrder() } diff --git a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt index 17f4d9bb17..0783f095d6 100644 --- a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt +++ b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt @@ -2,9 +2,9 @@ package com.simprints.feature.enrollast import androidx.annotation.Keep import com.simprints.core.ExcludedFromGeneratedTestCoverageReports +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.common.ModalitySdkType -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential @@ -27,13 +27,12 @@ sealed class EnrolLastBiometricStepResult : StepParams { @Keep data class CaptureResult( - val referenceId: String, - val results: List, + val result: BiometricReferenceCapture, ) : EnrolLastBiometricStepResult() @Keep data class MatchResult( - val results: List, + val results: List, val sdk: ModalitySdkType, ) : EnrolLastBiometricStepResult() } diff --git a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModel.kt b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModel.kt index 04d7017473..f0ba95a7f1 100644 --- a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModel.kt +++ b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModel.kt @@ -12,7 +12,7 @@ import com.simprints.feature.enrollast.EnrolLastBiometricParams import com.simprints.feature.enrollast.EnrolLastBiometricStepResult import com.simprints.feature.enrollast.screen.EnrolLastState.ErrorType.GENERAL_ERROR import com.simprints.feature.enrollast.screen.model.CredentialDialogItem -import com.simprints.feature.enrollast.screen.usecase.BuildSubjectUseCase +import com.simprints.feature.enrollast.screen.usecase.BuildRecordUseCase import com.simprints.feature.enrollast.screen.usecase.CheckForDuplicateEnrolmentsUseCase import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential import com.simprints.feature.externalcredential.screens.search.model.toExternalCredential @@ -21,9 +21,9 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.event.domain.models.BiometricReferenceCreationEvent import com.simprints.infra.events.event.domain.models.EnrolmentEventV4 import com.simprints.infra.events.event.domain.models.ExternalCredentialCaptureValueEvent @@ -42,7 +42,7 @@ internal class EnrolLastBiometricViewModel @Inject constructor( private val enrolmentRecordRepository: EnrolmentRecordRepository, private val checkForDuplicateEnrolments: CheckForDuplicateEnrolmentsUseCase, private val tokenizationProcessor: TokenizationProcessor, - private val buildSubject: BuildSubjectUseCase, + private val buildSubject: BuildRecordUseCase, private val resetEnrolmentUpdateEventsFromSession: ResetExternalCredentialsInSessionUseCase, ) : ViewModel() { val finish: LiveData> @@ -103,7 +103,7 @@ internal class EnrolLastBiometricViewModel @Inject constructor( try { val subject = buildSubject(params, isAddingCredential = isAddingCredential) registerEvent(subject) - enrolmentRecordRepository.performActions(listOf(SubjectAction.Creation(subject)), project) + enrolmentRecordRepository.performActions(listOf(EnrolmentRecordAction.Creation(subject)), project) _finish.send(EnrolLastState.Success(subject.subjectId, scannedCredential?.toExternalCredential(subject.subjectId))) } catch (t: Throwable) { Simber.e("Enrolment failed", t, tag = ENROLMENT) @@ -130,7 +130,7 @@ internal class EnrolLastBiometricViewModel @Inject constructor( return enrolmentRecordRepository .load( - SubjectQuery( + EnrolmentRecordQuery( projectId = projectId, externalCredential = scannedCredential.credential, ), @@ -140,7 +140,7 @@ internal class EnrolLastBiometricViewModel @Inject constructor( private fun getPreviousEnrolmentResult(steps: List) = steps.filterIsInstance().firstOrNull() - private suspend fun registerEvent(subject: Subject) { + private suspend fun registerEvent(enrolmentRecord: EnrolmentRecord) { Simber.d("Register events for enrolments", tag = ENROLMENT) val events = eventRepository.getEventsInCurrentSession() @@ -159,10 +159,10 @@ internal class EnrolLastBiometricViewModel @Inject constructor( eventRepository.addOrUpdateEvent( EnrolmentEventV4( createdAt = timeHelper.now(), - subjectId = subject.subjectId, - projectId = subject.projectId, - moduleId = subject.moduleId, - attendantId = subject.attendantId, + subjectId = enrolmentRecord.subjectId, + projectId = enrolmentRecord.projectId, + moduleId = enrolmentRecord.moduleId, + attendantId = enrolmentRecord.attendantId, biometricReferenceIds = biometricReferenceIds, externalCredentialIds = externalCredentialIds, ), diff --git a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/BuildSubjectUseCase.kt b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/BuildRecordUseCase.kt similarity index 62% rename from feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/BuildSubjectUseCase.kt rename to feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/BuildRecordUseCase.kt index 2fc46b034f..551bf863ed 100644 --- a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/BuildSubjectUseCase.kt +++ b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/BuildRecordUseCase.kt @@ -1,26 +1,27 @@ package com.simprints.feature.enrollast.screen.usecase -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.core.tools.time.TimeHelper import com.simprints.feature.enrollast.EnrolLastBiometricParams import com.simprints.feature.enrollast.EnrolLastBiometricStepResult import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential import com.simprints.feature.externalcredential.screens.search.model.toExternalCredential -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import java.util.Date import java.util.UUID import javax.inject.Inject -internal class BuildSubjectUseCase @Inject constructor( +internal class BuildRecordUseCase @Inject constructor( private val timeHelper: TimeHelper, - private val subjectFactory: SubjectFactory, + private val enrolmentRecordFactory: EnrolmentRecordFactory, ) { operator fun invoke( params: EnrolLastBiometricParams, isAddingCredential: Boolean, - ): Subject { + ): EnrolmentRecord { val subjectId = UUID.randomUUID().toString() val externalCredentials = if (isAddingCredential) { getExternalCredentialResult(params.scannedCredential, subjectId)?.let(::listOf) ?: emptyList() @@ -29,15 +30,15 @@ internal class BuildSubjectUseCase @Inject constructor( } val captureResult = params.steps .filterIsInstance() - .flatMap { result -> result.results.map { toSample(result.referenceId, it) } } + .map { result -> result.result.toBiometricReference() } - return subjectFactory.buildSubject( + return enrolmentRecordFactory.buildEnrolmentRecord( subjectId = subjectId, projectId = params.projectId, attendantId = params.userId, moduleId = params.moduleId, createdAt = Date(timeHelper.now().ms), - samples = captureResult, + references = captureResult, externalCredentials = externalCredentials, ) } @@ -47,14 +48,15 @@ internal class BuildSubjectUseCase @Inject constructor( subjectId: String, ) = credential?.toExternalCredential(subjectId) - private fun toSample( - referenceId: String, - result: CaptureSample, - ) = Sample( - identifier = result.identifier, - template = result.template, - format = result.format, + private fun BiometricReferenceCapture.toBiometricReference() = BiometricReference( referenceId = referenceId, - modality = result.modality, + modality = modality, + format = format, + templates = templates.map { templateCapture -> + BiometricTemplate( + identifier = templateCapture.identifier, + template = templateCapture.template, + ) + }, ) } diff --git a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/CheckForDuplicateEnrolmentsUseCase.kt b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/CheckForDuplicateEnrolmentsUseCase.kt index 119e66be3f..ea631e1c3e 100644 --- a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/CheckForDuplicateEnrolmentsUseCase.kt +++ b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/usecase/CheckForDuplicateEnrolmentsUseCase.kt @@ -31,7 +31,9 @@ internal class CheckForDuplicateEnrolmentsUseCase @Inject constructor() { EnrolLastState.ErrorType.DUPLICATE_ENROLMENTS } - else -> null + else -> { + null + } } } @@ -48,7 +50,7 @@ internal class CheckForDuplicateEnrolmentsUseCase @Inject constructor() { ?.high ?.toFloat() ?: Float.MAX_VALUE - result.results.any { it.confidence >= threshold } + result.results.any { it.comparisonScore >= threshold } } private class MissingMatchResultException : IllegalStateException("No match response in duplicate check.") diff --git a/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModelTest.kt b/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModelTest.kt index c97c5dfa48..c2769f68b5 100644 --- a/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModelTest.kt +++ b/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricViewModelTest.kt @@ -8,7 +8,7 @@ import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.feature.enrollast.EnrolLastBiometricParams import com.simprints.feature.enrollast.EnrolLastBiometricStepResult -import com.simprints.feature.enrollast.screen.usecase.BuildSubjectUseCase +import com.simprints.feature.enrollast.screen.usecase.BuildRecordUseCase import com.simprints.feature.enrollast.screen.usecase.CheckForDuplicateEnrolmentsUseCase import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential import com.simprints.feature.externalcredential.usecase.ResetExternalCredentialsInSessionUseCase @@ -18,7 +18,7 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.events.event.domain.models.BiometricReferenceCreationEvent import com.simprints.infra.events.event.domain.models.BiometricReferenceCreationEvent.BiometricReferenceCreationPayload import com.simprints.infra.events.event.domain.models.EnrolmentEventV4 @@ -61,10 +61,10 @@ internal class EnrolLastBiometricViewModelTest { lateinit var checkForDuplicateEnrolments: CheckForDuplicateEnrolmentsUseCase @MockK - lateinit var buildSubject: BuildSubjectUseCase + lateinit var buildRecord: BuildRecordUseCase @MockK - lateinit var subject: Subject + lateinit var enrolmentRecord: EnrolmentRecord @MockK lateinit var scannedCredential: ScannedCredential @@ -93,7 +93,7 @@ internal class EnrolLastBiometricViewModelTest { ) coJustRun { resetEnrolmentUpdateEventsFromSession.invoke(any()) } - every { subject.subjectId } returns guidToEnrol + every { enrolmentRecord.subjectId } returns guidToEnrol viewModel = EnrolLastBiometricViewModel( timeHelper = timeHelper, @@ -102,7 +102,7 @@ internal class EnrolLastBiometricViewModelTest { enrolmentRecordRepository = enrolmentRecordRepository, checkForDuplicateEnrolments = checkForDuplicateEnrolments, tokenizationProcessor = tokenizationProcessor, - buildSubject = buildSubject, + buildSubject = buildRecord, resetEnrolmentUpdateEventsFromSession = resetEnrolmentUpdateEventsFromSession, ) } @@ -216,7 +216,7 @@ internal class EnrolLastBiometricViewModelTest { @Test fun `returns success when no duplicate enrolments`() = runTest { every { checkForDuplicateEnrolments.invoke(any(), any()) } returns null - coEvery { buildSubject.invoke(any(), any()) } returns subject + coEvery { buildRecord.invoke(any(), any()) } returns enrolmentRecord viewModel.enrolBiometric(createParams(listOf()), isAddingCredential = false) @@ -230,7 +230,7 @@ internal class EnrolLastBiometricViewModelTest { @Test fun `saves event and record when no duplicate enrolments`() = runTest { every { checkForDuplicateEnrolments.invoke(any(), any()) } returns null - coEvery { buildSubject.invoke(any(), any()) } returns subject + coEvery { buildRecord.invoke(any(), any()) } returns enrolmentRecord viewModel.enrolBiometric(createParams(listOf()), isAddingCredential = false) @@ -241,7 +241,7 @@ internal class EnrolLastBiometricViewModelTest { @Test fun `returns failure record saving fails`() = runTest { every { checkForDuplicateEnrolments.invoke(any(), any()) } returns null - coEvery { buildSubject.invoke(any(), any()) } returns subject + coEvery { buildRecord.invoke(any(), any()) } returns enrolmentRecord coEvery { enrolmentRecordRepository.performActions(any(), any()) } throws Exception() viewModel.enrolBiometric(createParams(listOf()), isAddingCredential = false) @@ -294,7 +294,7 @@ internal class EnrolLastBiometricViewModelTest { @Test fun `shows add credential dialog when scanned credential is linked to another subject`() = runTest { val decryptedCredential = "decryptedCredential".asTokenizableRaw() - coEvery { enrolmentRecordRepository.load(any()) } returns listOf(subject) + coEvery { enrolmentRecordRepository.load(any()) } returns listOf(enrolmentRecord) coEvery { configManager.getProject() } returns project coEvery { tokenizationProcessor.decrypt( @@ -320,14 +320,14 @@ internal class EnrolLastBiometricViewModelTest { assertThat(result).isNotNull() assertThat(result?.scannedCredential).isEqualTo(scannedCredential) assertThat(result?.displayedCredential).isEqualTo(decryptedCredential) - coVerify(exactly = 0) { buildSubject.invoke(any(), any()) } + coVerify(exactly = 0) { buildRecord.invoke(any(), any()) } coVerify(exactly = 0) { enrolmentRecordRepository.performActions(any(), any()) } } @Test fun `add credential dialog is not shown when there is no result`() = runTest { val decryptedCredential = "decryptedCredential".asTokenizableRaw() - coEvery { enrolmentRecordRepository.load(any()) } returns listOf(subject) + coEvery { enrolmentRecordRepository.load(any()) } returns listOf(enrolmentRecord) coEvery { configManager.getProject() } returns project coEvery { tokenizationProcessor.decrypt( @@ -345,7 +345,7 @@ internal class EnrolLastBiometricViewModelTest { @Test fun `add credential dialog is not shown when there are no credentials`() = runTest { val decryptedCredential = "decryptedCredential".asTokenizableRaw() - coEvery { enrolmentRecordRepository.load(any()) } returns listOf(subject) + coEvery { enrolmentRecordRepository.load(any()) } returns listOf(enrolmentRecord) coEvery { configManager.getProject() } returns project coEvery { tokenizationProcessor.decrypt( @@ -357,7 +357,7 @@ internal class EnrolLastBiometricViewModelTest { viewModel.onViewCreated( createParams( - steps = listOf(EnrolLastBiometricStepResult.EnrolLastBiometricsResult(subjectId = subject.subjectId)), + steps = listOf(EnrolLastBiometricStepResult.EnrolLastBiometricsResult(subjectId = enrolmentRecord.subjectId)), credentials = null, ), ) @@ -368,7 +368,7 @@ internal class EnrolLastBiometricViewModelTest { @Test fun `add credential dialog is not shown when credential is already linked to same subject`() = runTest { val decryptedCredential = "decryptedCredential".asTokenizableRaw() - coEvery { enrolmentRecordRepository.load(any()) } returns listOf(subject) + coEvery { enrolmentRecordRepository.load(any()) } returns listOf(enrolmentRecord) coEvery { configManager.getProject() } returns project coEvery { tokenizationProcessor.decrypt( @@ -381,7 +381,7 @@ internal class EnrolLastBiometricViewModelTest { viewModel.onViewCreated( createParams( listOf( - EnrolLastBiometricStepResult.EnrolLastBiometricsResult(subjectId = subject.subjectId), + EnrolLastBiometricStepResult.EnrolLastBiometricsResult(subjectId = enrolmentRecord.subjectId), ), ), ) diff --git a/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/BuildSubjectUseCaseTest.kt b/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/BuildRecordUseCaseTest.kt similarity index 55% rename from feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/BuildSubjectUseCaseTest.kt rename to feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/BuildRecordUseCaseTest.kt index 91f1ce72da..88deb5b8c0 100644 --- a/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/BuildSubjectUseCaseTest.kt +++ b/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/BuildRecordUseCaseTest.kt @@ -3,8 +3,9 @@ package com.simprints.feature.enrollast.screen.usecase import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.time.TimeHelper @@ -12,44 +13,44 @@ import com.simprints.core.tools.time.Timestamp import com.simprints.feature.enrollast.EnrolLastBiometricParams import com.simprints.feature.enrollast.EnrolLastBiometricStepResult import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import com.simprints.testtools.unit.EncodingUtilsImplForTests import io.mockk.* import io.mockk.impl.annotations.MockK import org.junit.Before import org.junit.Test -class BuildSubjectUseCaseTest { +class BuildRecordUseCaseTest { @MockK private lateinit var timeHelper: TimeHelper @MockK private lateinit var scannedCredential: ScannedCredential - private lateinit var useCase: BuildSubjectUseCase + private lateinit var useCase: BuildRecordUseCase - private lateinit var subjectFactory: SubjectFactory + private lateinit var enrolmentRecordFactory: EnrolmentRecordFactory @Before fun setUp() { MockKAnnotations.init(this, relaxed = true) every { timeHelper.now() }.returns(Timestamp(1L)) - subjectFactory = SubjectFactory( + enrolmentRecordFactory = EnrolmentRecordFactory( encodingUtils = EncodingUtilsImplForTests, timeHelper = timeHelper, ) - useCase = BuildSubjectUseCase(timeHelper = timeHelper, subjectFactory = subjectFactory) + useCase = BuildRecordUseCase(timeHelper = timeHelper, enrolmentRecordFactory = enrolmentRecordFactory) } @Test - fun `has no samples if no steps provided`() { + fun `has no references if no steps provided`() { val result = useCase(createParams(steps = emptyList(), scannedCredential = scannedCredential), isAddingCredential = false) - assertThat(result.samples).isEmpty() + assertThat(result.references).isEmpty() } @Test - fun `has no samples if no valid steps provided`() { + fun `has no references if no valid steps provided`() { val result = useCase( createParams( steps = listOf( @@ -62,22 +63,30 @@ class BuildSubjectUseCaseTest { isAddingCredential = false, ) - assertThat(result.samples).isEmpty() + assertThat(result.references).isEmpty() } @Test fun `maps first available fingerprint capture step results`() { val result = useCase( - createParams( + params = createParams( steps = listOf( EnrolLastBiometricStepResult.MatchResult(emptyList(), mockk()), EnrolLastBiometricStepResult.CaptureResult( - REFERENCE_ID, - listOf(mockFingerprintResults(SampleIdentifier.RIGHT_THUMB)), + BiometricReferenceCapture( + referenceId = REFERENCE_ID, + modality = Modality.FINGERPRINT, + format = "ISO_19794_2", + templates = listOf(mockFingerprintResults(TemplateIdentifier.RIGHT_THUMB)), + ), ), EnrolLastBiometricStepResult.CaptureResult( - REFERENCE_ID, - listOf(mockFingerprintResults(SampleIdentifier.LEFT_THUMB)), + BiometricReferenceCapture( + referenceId = REFERENCE_ID, + modality = Modality.FINGERPRINT, + format = "ISO_19794_2", + templates = listOf(mockFingerprintResults(TemplateIdentifier.LEFT_THUMB)), + ), ), ), scannedCredential = scannedCredential, @@ -85,8 +94,14 @@ class BuildSubjectUseCaseTest { isAddingCredential = false, ) - assertThat(result.samples).isNotEmpty() - assertThat(result.samples.first().identifier).isEqualTo(SampleIdentifier.RIGHT_THUMB) + assertThat(result.references).isNotEmpty() + assertThat( + result.references + .first() + .templates + .first() + .identifier, + ).isEqualTo(TemplateIdentifier.RIGHT_THUMB) } @Test @@ -95,18 +110,22 @@ class BuildSubjectUseCaseTest { createParams( steps = listOf( EnrolLastBiometricStepResult.CaptureResult( - REFERENCE_ID, - listOf( - mockFingerprintResults(SampleIdentifier.RIGHT_5TH_FINGER), - mockFingerprintResults(SampleIdentifier.RIGHT_4TH_FINGER), - mockFingerprintResults(SampleIdentifier.RIGHT_3RD_FINGER), - mockFingerprintResults(SampleIdentifier.RIGHT_INDEX_FINGER), - mockFingerprintResults(SampleIdentifier.RIGHT_THUMB), - mockFingerprintResults(SampleIdentifier.LEFT_THUMB), - mockFingerprintResults(SampleIdentifier.LEFT_INDEX_FINGER), - mockFingerprintResults(SampleIdentifier.LEFT_3RD_FINGER), - mockFingerprintResults(SampleIdentifier.LEFT_4TH_FINGER), - mockFingerprintResults(SampleIdentifier.LEFT_5TH_FINGER), + BiometricReferenceCapture( + referenceId = REFERENCE_ID, + modality = Modality.FINGERPRINT, + format = "ISO_19794_2", + templates = listOf( + mockFingerprintResults(TemplateIdentifier.RIGHT_5TH_FINGER), + mockFingerprintResults(TemplateIdentifier.RIGHT_4TH_FINGER), + mockFingerprintResults(TemplateIdentifier.RIGHT_3RD_FINGER), + mockFingerprintResults(TemplateIdentifier.RIGHT_INDEX_FINGER), + mockFingerprintResults(TemplateIdentifier.RIGHT_THUMB), + mockFingerprintResults(TemplateIdentifier.LEFT_THUMB), + mockFingerprintResults(TemplateIdentifier.LEFT_INDEX_FINGER), + mockFingerprintResults(TemplateIdentifier.LEFT_3RD_FINGER), + mockFingerprintResults(TemplateIdentifier.LEFT_4TH_FINGER), + mockFingerprintResults(TemplateIdentifier.LEFT_5TH_FINGER), + ), ), ), ), @@ -115,8 +134,12 @@ class BuildSubjectUseCaseTest { isAddingCredential = false, ) - assertThat(result.samples).isNotEmpty() - assertThat(result.samples.size).isEqualTo(10) + assertThat(result.references.size).isEqualTo(1) + assertThat( + result.references + .first() + .templates.size, + ).isEqualTo(10) } @Test @@ -125,16 +148,30 @@ class BuildSubjectUseCaseTest { params = createParams( listOf( EnrolLastBiometricStepResult.MatchResult(emptyList(), mockk()), - EnrolLastBiometricStepResult.CaptureResult(REFERENCE_ID, listOf(mockFaceResults("first"))), - EnrolLastBiometricStepResult.CaptureResult(REFERENCE_ID, listOf(mockFaceResults("second"))), + EnrolLastBiometricStepResult.CaptureResult( + BiometricReferenceCapture( + referenceId = REFERENCE_ID, + modality = Modality.FACE, + format = "first", + templates = listOf(mockFaceResults()), + ), + ), + EnrolLastBiometricStepResult.CaptureResult( + BiometricReferenceCapture( + referenceId = REFERENCE_ID, + modality = Modality.FACE, + format = "second", + templates = listOf(mockFaceResults()), + ), + ), ), scannedCredential = scannedCredential, ), isAddingCredential = false, ) - assertThat(result.samples).isNotEmpty() - assertThat(result.samples.first().format).isEqualTo("first") + assertThat(result.references).isNotEmpty() + assertThat(result.references.first().format).isEqualTo("first") } @Test @@ -178,19 +215,15 @@ class BuildSubjectUseCaseTest { scannedCredential = scannedCredential, ) - private fun mockFingerprintResults(finger: SampleIdentifier) = CaptureSample( + private fun mockFingerprintResults(finger: TemplateIdentifier) = BiometricTemplateCapture( captureEventId = "eventId", identifier = finger, template = byteArrayOf(), - format = "ISO_19794_2", - modality = Modality.FINGERPRINT, ) - private fun mockFaceResults(format: String) = CaptureSample( + private fun mockFaceResults() = BiometricTemplateCapture( captureEventId = "eventId", template = byteArrayOf(), - format = format, - modality = Modality.FACE, ) companion object { diff --git a/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/CheckDuplicateEnrolmentsErrorsUseCaseTest.kt b/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/CheckDuplicateEnrolmentsErrorsUseCaseTest.kt index 2aa18828c5..300f5e9b8f 100644 --- a/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/CheckDuplicateEnrolmentsErrorsUseCaseTest.kt +++ b/feature/enrol-last-biometric/src/test/java/com/simprints/feature/enrollast/screen/usecase/CheckDuplicateEnrolmentsErrorsUseCaseTest.kt @@ -1,7 +1,7 @@ package com.simprints.feature.enrollast.screen.usecase import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.feature.enrollast.EnrolLastBiometricStepResult import com.simprints.feature.enrollast.screen.EnrolLastState import com.simprints.infra.config.store.models.DecisionPolicy @@ -144,7 +144,7 @@ class CheckDuplicateEnrolmentsErrorsUseCaseTest { } } - private fun matchResult(confidence: Float) = MatchComparisonResult("subjectId", confidence) + private fun matchResult(confidence: Float) = ComparisonResult("subjectId", confidence) companion object { private const val LOW_CONFIDENCE = 50f diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialContract.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialContract.kt index d32383efb6..d27bb315fc 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialContract.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialContract.kt @@ -3,8 +3,7 @@ package com.simprints.feature.externalcredential import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.feature.externalcredential.model.ExternalCredentialParams @ExcludedFromGeneratedTestCoverageReports("Navigation class") @@ -15,13 +14,11 @@ object ExternalCredentialContract { subjectId: String?, flowType: FlowType, ageGroup: AgeGroup?, - probeReferenceId: String? = null, - samples: Map> = emptyMap(), + probeReferences: List = emptyList(), ) = ExternalCredentialParams( subjectId = subjectId, flowType = flowType, ageGroup = ageGroup, - probeReferenceId = probeReferenceId, - samples = samples, + probeReferences = probeReferences, ) } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/CredentialMatch.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/CredentialMatch.kt index 1580892ff4..95c27c7f15 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/CredentialMatch.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/CredentialMatch.kt @@ -3,7 +3,7 @@ package com.simprints.feature.externalcredential.model import androidx.annotation.Keep import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import com.simprints.core.domain.common.ModalitySdkType -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.domain.step.StepResult import com.simprints.core.domain.tokenization.TokenizableString @@ -11,9 +11,9 @@ import com.simprints.core.domain.tokenization.TokenizableString @ExcludedFromGeneratedTestCoverageReports("Data class") data class CredentialMatch( val credential: TokenizableString.Tokenized, - val matchResult: MatchComparisonResult, + val comparisonResult: ComparisonResult, val verificationThreshold: Float, val bioSdk: ModalitySdkType, ) : StepResult { - val isVerificationSuccessful = matchResult.confidence >= verificationThreshold + val isVerificationSuccessful = comparisonResult.comparisonScore >= verificationThreshold } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/ExternalCredentialParams.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/ExternalCredentialParams.kt index 85db7e24c1..862eae7dfb 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/ExternalCredentialParams.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/model/ExternalCredentialParams.kt @@ -4,8 +4,7 @@ import androidx.annotation.Keep import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.step.StepParams @Keep @@ -14,6 +13,5 @@ data class ExternalCredentialParams( val subjectId: String?, val flowType: FlowType, val ageGroup: AgeGroup?, - val probeReferenceId: String?, - val samples: Map>, + val probeReferences: List, ) : StepParams diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt index 1ddee3bb08..7e2a54bcec 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt @@ -23,7 +23,7 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.event.domain.models.ExternalCredentialConfirmationEvent.ExternalCredentialConfirmationResult import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -106,9 +106,14 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( searchState: SearchState, flowType: FlowType, ): Int? = when (searchState) { - SearchState.Searching -> null // button is not displayed during search + SearchState.Searching -> null + + // button is not displayed during search is SearchState.CredentialLinked -> when (flowType) { - FlowType.ENROL -> IDR.string.mfid_action_enrol_anyway + FlowType.ENROL -> { + IDR.string.mfid_action_enrol_anyway + } + else -> { if (searchState.hasSuccessfulVerifications) { IDR.string.mfid_action_go_to_record @@ -141,7 +146,7 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( credential: TokenizableString.Tokenized, ) { updateState { it.copy(searchState = SearchState.Searching) } - val candidates = enrolmentRecordRepository.load(SubjectQuery(projectId = project.id, externalCredential = credential)) + val candidates = enrolmentRecordRepository.load(EnrolmentRecordQuery(projectId = project.id, externalCredential = credential)) val startTime = timeHelper.now() when { @@ -165,8 +170,11 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( * alpha-numeric, while the NHIS card memberships contain only digits. */ fun getKeyBoardInputType() = when (scannedCredential.credentialType) { - ExternalCredentialType.NHISCard -> InputType.TYPE_CLASS_NUMBER // NHIS card membership contains only numbers + ExternalCredentialType.NHISCard -> InputType.TYPE_CLASS_NUMBER + + // NHIS card membership contains only numbers ExternalCredentialType.GhanaIdCard -> InputType.TYPE_CLASS_TEXT + ExternalCredentialType.QRCode -> InputType.TYPE_CLASS_TEXT } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCase.kt index 13413bde66..53fa6355ad 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCase.kt @@ -2,12 +2,11 @@ package com.simprints.feature.externalcredential.screens.search.usecase import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.infra.config.store.models.ProjectConfiguration import com.simprints.infra.config.store.models.getSdkListForAgeGroup import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.matching.MatchParams import javax.inject.Inject @@ -15,20 +14,19 @@ internal class CreateMatchParamsUseCase @Inject constructor() { operator fun invoke( candidateSubjectId: String, flowType: FlowType, - probeReferenceId: String?, projectConfiguration: ProjectConfiguration, - samples: Map>, + probeReferences: List, ageGroup: AgeGroup?, - ): List = projectConfiguration.general.matchingModalities - .map { modality -> - val modalityProbes = samples[modality].orEmpty() - projectConfiguration.getSdkListForAgeGroup(modality, ageGroup).map { + ): List = probeReferences + .filter { it.modality in projectConfiguration.general.matchingModalities } + .associateWith { projectConfiguration.getSdkListForAgeGroup(it.modality, ageGroup) } + .map { (probeReference, sdksPerModality) -> + sdksPerModality.map { MatchParams( - probeReferenceId = probeReferenceId.orEmpty(), flowType = flowType, - queryForCandidates = SubjectQuery(subjectId = candidateSubjectId), + queryForCandidates = EnrolmentRecordQuery(subjectId = candidateSubjectId), bioSdk = it, - probeSamples = modalityProbes, + probeReference = probeReference, biometricDataSource = BiometricDataSource.Simprints, // [MS-1167] No CoSync in initial MF-ID implementation ) } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCase.kt index 6aba463c74..fc74111c85 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCase.kt @@ -8,7 +8,7 @@ import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.store.models.ProjectConfiguration import com.simprints.infra.config.store.models.getModalitySdkConfig -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.matching.usecase.FaceMatcherUseCase import com.simprints.infra.matching.usecase.FingerprintMatcherUseCase import com.simprints.infra.matching.usecase.MatcherUseCase.MatcherState @@ -21,7 +21,7 @@ internal class MatchCandidatesUseCase @Inject constructor( private val fingerprintMatcher: FingerprintMatcherUseCase, ) { suspend operator fun invoke( - candidates: List, + candidates: List, credential: TokenizableString.Tokenized, externalCredentialParams: ExternalCredentialParams, project: Project, @@ -30,9 +30,8 @@ internal class MatchCandidatesUseCase @Inject constructor( val matchParams = createMatchParamsUseCase( candidateSubjectId = candidate.subjectId, flowType = externalCredentialParams.flowType, - probeReferenceId = externalCredentialParams.probeReferenceId, projectConfiguration = projectConfig, - samples = externalCredentialParams.samples, + probeReferences = externalCredentialParams.probeReferences, ageGroup = externalCredentialParams.ageGroup, ) matchParams @@ -49,7 +48,7 @@ internal class MatchCandidatesUseCase @Inject constructor( lastMatchSuccess?.comparisonResults?.map { result -> CredentialMatch( credential = credential, - matchResult = result, + comparisonResult = result, verificationThreshold = matchThreshold, bioSdk = matchParam.bioSdk, ) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt index c388506cb1..4deb273920 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ExternalCredentialEventTrackerUseCase.kt @@ -10,7 +10,7 @@ import com.simprints.feature.externalcredential.screens.search.model.toExternalC import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.events.event.domain.models.ExternalCredentialCaptureEvent import com.simprints.infra.events.event.domain.models.ExternalCredentialCaptureValueEvent import com.simprints.infra.events.event.domain.models.ExternalCredentialConfirmationEvent @@ -31,7 +31,7 @@ internal class ExternalCredentialEventTrackerUseCase @Inject constructor( suspend fun saveSearchEvent( startTime: Timestamp, externalCredentialId: String, - candidates: List, + candidates: List, ) { eventRepository.addOrUpdateEvent( ExternalCredentialSearchEvent( diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCase.kt index fd9a461c04..c28047699e 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCase.kt @@ -6,7 +6,7 @@ import com.simprints.feature.externalcredential.screens.search.model.ScannedCred import com.simprints.feature.externalcredential.screens.search.model.toExternalCredential import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.events.event.domain.models.EnrolmentUpdateEvent import com.simprints.infra.events.session.SessionEventRepository import kotlinx.coroutines.CoroutineScope @@ -29,7 +29,7 @@ class ResetExternalCredentialsInSessionUseCase @Inject() constructor( // Within a session the external credentials can be linked to a single subject only, // therefore we must ensure that on consecutive confirmation the previous links are reverted. val credentialsToRemove = enrolmentUpdateEvents.map { - SubjectAction.Update( + EnrolmentRecordAction.Update( subjectId = it.payload.subjectId, samplesToAdd = emptyList(), referenceIdsToRemove = emptyList(), @@ -41,7 +41,7 @@ class ResetExternalCredentialsInSessionUseCase @Inject() constructor( val validSubjectId = subjectId.takeIf { it.isValidGuid() } val credentialsToAdd = if (validSubjectId != null && scannedCredential != null) { listOf( - SubjectAction.Update( + EnrolmentRecordAction.Update( subjectId = subjectId, samplesToAdd = emptyList(), referenceIdsToRemove = emptyList(), diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModelTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModelTest.kt index 05ef90863b..8243ddeec9 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModelTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModelTest.kt @@ -1,7 +1,7 @@ package com.simprints.feature.externalcredential.screens.controller import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.* import com.jraska.livedata.test import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.externalcredential.ExternalCredentialType @@ -17,12 +17,8 @@ import com.simprints.feature.externalcredential.usecase.ExternalCredentialEventT import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.events.event.domain.models.ExternalCredentialSelectionEvent import com.simprints.testtools.common.coroutines.TestCoroutineRule -import io.mockk.MockKAnnotations -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.every +import io.mockk.* import io.mockk.impl.annotations.MockK -import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule @@ -227,7 +223,6 @@ internal class ExternalCredentialViewModelTest { subjectId = subjectId, flowType = flowType, ageGroup = null, - probeReferenceId = null, - samples = emptyMap(), + probeReferences = emptyList(), ) } diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt index 11a3fb647d..b7a7e265e5 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt @@ -22,7 +22,7 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.testtools.common.coroutines.TestCoroutineRule import io.mockk.* import io.mockk.impl.annotations.MockK @@ -55,7 +55,7 @@ internal class ExternalCredentialSearchViewModelTest { lateinit var projectConfig: ProjectConfiguration @MockK - lateinit var subject: Subject + lateinit var enrolmentRecord: EnrolmentRecord @MockK lateinit var candidateMatch: CredentialMatch @@ -129,7 +129,7 @@ internal class ExternalCredentialSearchViewModelTest { fun `initial state searches and finds linked credential`() = runTest { val decryptedCredential = mockk() coEvery { tokenizationProcessor.decrypt(any(), TokenKeyType.ExternalCredential, project) } returns decryptedCredential - coEvery { enrolmentRecordRepository.load(any()) } returns listOf(subject) + coEvery { enrolmentRecordRepository.load(any()) } returns listOf(enrolmentRecord) coEvery { matchCandidatesUseCase(any(), any(), any(), any(), any()) } returns listOf(candidateMatch) diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCaseTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCaseTest.kt index 26b051d547..b7cfc014b5 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCaseTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/CreateMatchParamsUseCaseTest.kt @@ -4,7 +4,7 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.models.GeneralConfiguration @@ -25,10 +25,10 @@ internal class CreateMatchParamsUseCaseTest { private val ageGroup = AgeGroup(25, 30) @MockK - private lateinit var faceSample: CaptureSample + private lateinit var faceCapture: BiometricReferenceCapture @MockK - private lateinit var fingerprintSample: CaptureSample + private lateinit var fingerprintCapture: BiometricReferenceCapture @MockK private lateinit var generalConfiguration: GeneralConfiguration @@ -40,6 +40,10 @@ internal class CreateMatchParamsUseCaseTest { fun setUp() { MockKAnnotations.init(this, relaxed = true) mockkStatic("com.simprints.infra.config.store.models.ProjectConfigurationKt") + + every { faceCapture.modality } returns Modality.FACE + every { fingerprintCapture.modality } returns Modality.FINGERPRINT + every { projectConfiguration.general } returns generalConfiguration } @@ -54,19 +58,17 @@ internal class CreateMatchParamsUseCaseTest { val result = useCase( candidateSubjectId = subjectId, flowType = flowType, - probeReferenceId = probeReferenceId, projectConfiguration = projectConfiguration, - samples = mapOf(Modality.FACE to listOf(faceSample)), + probeReferences = listOf(faceCapture), ageGroup = ageGroup, ) assertThat(result).hasSize(2) result.forEach { matchParams -> - assertThat(matchParams.probeReferenceId).isEqualTo(probeReferenceId) assertThat(matchParams.flowType).isEqualTo(flowType) assertThat(matchParams.queryForCandidates.subjectId).isEqualTo(subjectId) assertThat(matchParams.biometricDataSource).isEqualTo(BiometricDataSource.Simprints) - assertThat(matchParams.probeSamples).containsExactly(faceSample) + assertThat(matchParams.probeReference).isEqualTo(faceCapture) assertThat(matchParams.bioSdk).isNotNull() } assertThat(result[0].bioSdk).isEqualTo(FaceConfiguration.BioSdk.RANK_ONE) @@ -84,19 +86,17 @@ internal class CreateMatchParamsUseCaseTest { val result = useCase( candidateSubjectId = subjectId, flowType = flowType, - probeReferenceId = probeReferenceId, projectConfiguration = projectConfiguration, - samples = mapOf(Modality.FINGERPRINT to listOf(fingerprintSample)), + probeReferences = listOf(fingerprintCapture), ageGroup = ageGroup, ) assertThat(result).hasSize(2) result.forEach { matchParams -> - assertThat(matchParams.probeReferenceId).isEqualTo(probeReferenceId) assertThat(matchParams.flowType).isEqualTo(flowType) assertThat(matchParams.queryForCandidates.subjectId).isEqualTo(subjectId) assertThat(matchParams.biometricDataSource).isEqualTo(BiometricDataSource.Simprints) - assertThat(matchParams.probeSamples).containsExactly(fingerprintSample) + assertThat(matchParams.probeReference).isEqualTo(fingerprintCapture) assertThat(matchParams.bioSdk).isNotNull() } assertThat(result[0].bioSdk).isEqualTo(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER) @@ -119,12 +119,8 @@ internal class CreateMatchParamsUseCaseTest { val result = useCase( candidateSubjectId = subjectId, flowType = flowType, - probeReferenceId = probeReferenceId, projectConfiguration = projectConfiguration, - samples = mapOf( - Modality.FINGERPRINT to listOf(fingerprintSample), - Modality.FACE to listOf(faceSample), - ), + probeReferences = listOf(fingerprintCapture, faceCapture), ageGroup = ageGroup, ) @@ -133,12 +129,12 @@ internal class CreateMatchParamsUseCaseTest { val faceMatch = result.find { it.bioSdk is FaceConfiguration.BioSdk } assertThat(faceMatch).isNotNull() assertThat(faceMatch?.bioSdk).isEqualTo(FaceConfiguration.BioSdk.RANK_ONE) - assertThat(faceMatch?.probeSamples).containsExactly(faceSample) + assertThat(faceMatch?.probeReference).isEqualTo(faceCapture) val fingerprintMatch = result.find { it.bioSdk is FingerprintConfiguration.BioSdk } assertThat(fingerprintMatch).isNotNull() assertThat(fingerprintMatch?.bioSdk).isEqualTo(FingerprintConfiguration.BioSdk.NEC) - assertThat(fingerprintMatch?.probeSamples).containsExactly(fingerprintSample) + assertThat(fingerprintMatch?.probeReference).isEqualTo(fingerprintCapture) } @Test @@ -151,9 +147,8 @@ internal class CreateMatchParamsUseCaseTest { val result = useCase( candidateSubjectId = subjectId, flowType = flowType, - probeReferenceId = probeReferenceId, projectConfiguration = projectConfiguration, - samples = mapOf(Modality.FACE to listOf(faceSample)), + probeReferences = listOf(faceCapture), ageGroup = null, ) @@ -167,12 +162,8 @@ internal class CreateMatchParamsUseCaseTest { val result = useCase( candidateSubjectId = subjectId, flowType = flowType, - probeReferenceId = probeReferenceId, projectConfiguration = projectConfiguration, - samples = mapOf( - Modality.FINGERPRINT to listOf(fingerprintSample), - Modality.FACE to listOf(faceSample), - ), + probeReferences = listOf(fingerprintCapture, faceCapture), ageGroup = ageGroup, ) @@ -187,22 +178,20 @@ internal class CreateMatchParamsUseCaseTest { ) every { generalConfiguration.matchingModalities } returns listOf(Modality.FACE) - val faceSamples = listOf(faceSample, mockk(relaxed = true)) + val faceSamples = listOf( + faceCapture, + mockk { every { modality } returns Modality.FACE }, + ) val result = useCase( candidateSubjectId = subjectId, flowType = flowType, - probeReferenceId = probeReferenceId, projectConfiguration = projectConfiguration, - samples = mapOf( - Modality.FACE to faceSamples, - ), + probeReferences = faceSamples, ageGroup = ageGroup, ) - assertThat(result).hasSize(2) - result.forEach { matchParams -> - assertThat(matchParams.probeSamples).hasSize(2) - } + // 2 captures * 2 sdks + assertThat(result).hasSize(4) } } diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCaseTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCaseTest.kt index 68b9f80e6b..2e75d88747 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCaseTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/usecase/MatchCandidatesUseCaseTest.kt @@ -1,11 +1,10 @@ package com.simprints.feature.externalcredential.screens.search.usecase import com.google.common.truth.Truth.* +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.feature.externalcredential.model.ExternalCredentialParams import com.simprints.infra.config.store.models.FaceConfiguration @@ -13,7 +12,7 @@ import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.store.models.ProjectConfiguration import com.simprints.infra.config.store.models.getModalitySdkConfig -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.matching.MatchParams import com.simprints.infra.matching.usecase.FaceMatcherUseCase import com.simprints.infra.matching.usecase.FingerprintMatcherUseCase @@ -38,7 +37,7 @@ internal class MatchCandidatesUseCaseTest { private lateinit var fingerprintMatcher: FingerprintMatcherUseCase @MockK - private lateinit var subject: Subject + private lateinit var enrolmentRecord: EnrolmentRecord @MockK private lateinit var project: Project @@ -62,16 +61,16 @@ internal class MatchCandidatesUseCaseTest { private lateinit var fingerprintSdkConfig: FingerprintConfiguration.FingerprintSdkConfiguration @MockK - private lateinit var matchResultItem: MatchComparisonResult + private lateinit var matchResultItem: ComparisonResult @MockK private lateinit var matchParams: MatchParams @MockK - private lateinit var faceSample: CaptureSample + private lateinit var faceCapture: BiometricReferenceCapture @MockK - private lateinit var fingerprintSample: CaptureSample + private lateinit var fingerprintCapture: BiometricReferenceCapture @MockK private lateinit var ageGroup: AgeGroup @@ -93,22 +92,17 @@ internal class MatchCandidatesUseCaseTest { fingerprintMatcher = fingerprintMatcher, ) - every { subject.subjectId } returns subjectId - every { externalCredentialParams.probeReferenceId } returns probeReferenceId + every { enrolmentRecord.subjectId } returns subjectId every { externalCredentialParams.flowType } returns FlowType.VERIFY - every { externalCredentialParams.samples } returns mapOf( - Modality.FACE to listOf(faceSample), - Modality.FINGERPRINT to listOf(fingerprintSample), - ) + every { externalCredentialParams.probeReferences } returns listOf(faceCapture, fingerprintCapture) every { externalCredentialParams.ageGroup } returns ageGroup coEvery { createMatchParamsUseCase( candidateSubjectId = any(), flowType = any(), - probeReferenceId = any(), projectConfiguration = any(), - samples = any(), + probeReferences = any(), ageGroup = any(), ) } returns listOf(matchParams) @@ -125,10 +119,10 @@ internal class MatchCandidatesUseCaseTest { private fun initMatchParams(isFace: Boolean) { if (isFace) { - every { matchParams.probeSamples } returns listOf(faceSample) + every { matchParams.probeReference } returns faceCapture every { matchParams.bioSdk } returns FaceConfiguration.BioSdk.RANK_ONE } else { - every { matchParams.probeSamples } returns listOf(fingerprintSample) + every { matchParams.probeReference } returns fingerprintCapture every { matchParams.bioSdk } returns FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER } } @@ -137,7 +131,7 @@ internal class MatchCandidatesUseCaseTest { fun `returns face matches when face samples present`() = runTest { initMatchParams(isFace = true) val result = useCase.invoke( - candidates = listOf(subject), + candidates = listOf(enrolmentRecord), credential = credential, externalCredentialParams = externalCredentialParams, project = project, @@ -146,7 +140,7 @@ internal class MatchCandidatesUseCaseTest { assertThat(result).hasSize(1) assertThat(result[0].credential).isEqualTo(credential) - assertThat(result[0].matchResult).isEqualTo(matchResultItem) + assertThat(result[0].comparisonResult).isEqualTo(matchResultItem) assertThat(result[0].verificationThreshold).isEqualTo(verificationMatchThreshold) assertThat(result[0].bioSdk).isEqualTo(FaceConfiguration.BioSdk.RANK_ONE) } @@ -155,7 +149,7 @@ internal class MatchCandidatesUseCaseTest { fun `returns fingerprint matches when no face samples present`() = runTest { initMatchParams(isFace = false) val result = useCase.invoke( - candidates = listOf(subject), + candidates = listOf(enrolmentRecord), credential = credential, externalCredentialParams = externalCredentialParams, project = project, @@ -164,7 +158,7 @@ internal class MatchCandidatesUseCaseTest { assertThat(result).hasSize(1) assertThat(result[0].credential).isEqualTo(credential) - assertThat(result[0].matchResult).isEqualTo(matchResultItem) + assertThat(result[0].comparisonResult).isEqualTo(matchResultItem) assertThat(result[0].verificationThreshold).isEqualTo(verificationMatchThreshold) assertThat(result[0].bioSdk).isEqualTo(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER) } @@ -188,7 +182,7 @@ internal class MatchCandidatesUseCaseTest { every { projectConfig.getModalitySdkConfig(FaceConfiguration.BioSdk.RANK_ONE) } returns null val result = useCase.invoke( - candidates = listOf(subject), + candidates = listOf(enrolmentRecord), credential = credential, externalCredentialParams = externalCredentialParams, project = project, @@ -204,7 +198,7 @@ internal class MatchCandidatesUseCaseTest { every { projectConfig.getModalitySdkConfig(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER) } returns null val result = useCase.invoke( - candidates = listOf(subject), + candidates = listOf(enrolmentRecord), credential = credential, externalCredentialParams = externalCredentialParams, project = project, @@ -219,7 +213,7 @@ internal class MatchCandidatesUseCaseTest { initMatchParams(isFace = true) every { faceSdkConfig.verificationMatchThreshold } returns null val result = useCase( - candidates = listOf(subject), + candidates = listOf(enrolmentRecord), credential = credential, externalCredentialParams = externalCredentialParams, project = project, @@ -233,7 +227,7 @@ internal class MatchCandidatesUseCaseTest { initMatchParams(isFace = false) every { fingerprintSdkConfig.verificationMatchThreshold } returns null val result = useCase( - candidates = listOf(subject), + candidates = listOf(enrolmentRecord), credential = credential, externalCredentialParams = externalCredentialParams, project = project, diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCaseTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCaseTest.kt index 4eaf657dd3..2245dd9818 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCaseTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/usecase/ResetExternalCredentialsInSessionUseCaseTest.kt @@ -9,7 +9,7 @@ import com.simprints.feature.externalcredential.screens.search.model.ScannedCred import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.events.event.domain.models.EnrolmentUpdateEvent import com.simprints.infra.events.event.domain.models.ExternalCredentialSelectionEvent import com.simprints.infra.events.session.SessionEventRepository @@ -68,12 +68,12 @@ internal class ResetExternalCredentialsInSessionUseCaseTest { useCase(scannedCredential, SUBJECT_ID) - val actionsSlot = slot>() + val actionsSlot = slot>() coVerify { enrolmentRecordRepository.performActions(capture(actionsSlot), project) } val actions = actionsSlot.captured assertThat(actions).hasSize(1) - val updateAction = actions.first() as SubjectAction.Update + val updateAction = actions.first() as EnrolmentRecordAction.Update assertThat(updateAction.subjectId).isEqualTo(SUBJECT_ID) assertThat(updateAction.externalCredentialsToAdd).hasSize(1) assertThat(updateAction.samplesToAdd).isEmpty() @@ -86,10 +86,10 @@ internal class ResetExternalCredentialsInSessionUseCaseTest { useCase(scannedCredential, SUBJECT_ID) - val actionsSlot = slot>() + val actionsSlot = slot>() coVerify { enrolmentRecordRepository.performActions(capture(actionsSlot), project) } - val updateAction = actionsSlot.captured.first() as SubjectAction.Update + val updateAction = actionsSlot.captured.first() as EnrolmentRecordAction.Update val addedCredential = updateAction.externalCredentialsToAdd.first() assertThat(addedCredential.value).isEqualTo(CREDENTIAL) assertThat(addedCredential.type).isEqualTo(CREDENTIAL_TYE) @@ -117,16 +117,16 @@ internal class ResetExternalCredentialsInSessionUseCaseTest { subjectId = SUBJECT_ID, ) - val actionsSlot = slot>() + val actionsSlot = slot>() coVerify { enrolmentRecordRepository.performActions(capture(actionsSlot), project) } // Remove actions come first - val removeAction = actionsSlot.captured.first() as SubjectAction.Update + val removeAction = actionsSlot.captured.first() as EnrolmentRecordAction.Update assertThat(removeAction.subjectId).isEqualTo("subject-1") assertThat(removeAction.externalCredentialsToAdd).isEmpty() assertThat(removeAction.externalCredentialIdsToRemove).containsExactly("credentia-1") // Additions come after - val addAction = actionsSlot.captured.last() as SubjectAction.Update + val addAction = actionsSlot.captured.last() as EnrolmentRecordAction.Update assertThat(addAction.externalCredentialsToAdd).isNotEmpty() assertThat(addAction.externalCredentialIdsToRemove).isEmpty() } @@ -154,7 +154,7 @@ internal class ResetExternalCredentialsInSessionUseCaseTest { subjectId = "none_selected", ) - val actionsSlot = slot>() + val actionsSlot = slot>() coVerify { enrolmentRecordRepository.performActions(capture(actionsSlot), project) } assertThat(actionsSlot.captured).isEmpty() diff --git a/feature/fetch-subject/src/main/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCase.kt b/feature/fetch-subject/src/main/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCase.kt index 2340c9ab41..fc3ed96f20 100644 --- a/feature/fetch-subject/src/main/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCase.kt +++ b/feature/fetch-subject/src/main/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCase.kt @@ -2,7 +2,7 @@ package com.simprints.feature.fetchsubject.screen.usecase import com.simprints.feature.fetchsubject.screen.FetchSubjectState import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.eventsync.EventSyncManager import com.simprints.infra.logging.Simber import com.simprints.infra.network.ConnectivityTracker @@ -47,7 +47,7 @@ internal class FetchSubjectUseCase @Inject constructor( private suspend fun loadFromDatabase( projectId: String, subjectId: String, - ) = enrolmentRecordRepository.load(SubjectQuery(projectId, subjectId)).firstOrNull() + ) = enrolmentRecordRepository.load(EnrolmentRecordQuery(projectId, subjectId)).firstOrNull() private fun notFoundState() = if (connectivityTracker.isConnected()) { FetchSubjectState.NotFound diff --git a/feature/fetch-subject/src/test/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCaseTest.kt b/feature/fetch-subject/src/test/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCaseTest.kt index 697bfb9f65..b89878aa88 100644 --- a/feature/fetch-subject/src/test/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCaseTest.kt +++ b/feature/fetch-subject/src/test/java/com/simprints/feature/fetchsubject/screen/usecase/FetchSubjectUseCaseTest.kt @@ -3,8 +3,8 @@ package com.simprints.feature.fetchsubject.screen.usecase import com.google.common.truth.Truth.assertThat import com.simprints.feature.fetchsubject.screen.FetchSubjectState import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.eventsync.EventSyncManager import com.simprints.infra.network.ConnectivityTracker import io.mockk.MockKAnnotations @@ -27,7 +27,7 @@ internal class FetchSubjectUseCaseTest { lateinit var eventSyncManager: EventSyncManager @MockK - lateinit var subject: Subject + lateinit var enrolmentRecord: EnrolmentRecord private lateinit var useCase: FetchSubjectUseCase @@ -40,17 +40,17 @@ internal class FetchSubjectUseCaseTest { @Test fun `fetch should check local DB first`() = runTest { - coEvery { enrolmentRecordRepository.load(any()) } returns listOf(subject) + coEvery { enrolmentRecordRepository.load(any()) } returns listOf(enrolmentRecord) val result = useCase(DEFAULT_PROJECT_ID, DEFAULT_SUBJECT_ID, "") - coVerify { enrolmentRecordRepository.load(SubjectQuery(DEFAULT_PROJECT_ID, DEFAULT_SUBJECT_ID)) } + coVerify { enrolmentRecordRepository.load(EnrolmentRecordQuery(DEFAULT_PROJECT_ID, DEFAULT_SUBJECT_ID)) } assertThat(result).isInstanceOf(FetchSubjectState.FoundLocal::class.java) } @Test fun `fetch should not check remote if present in local DB`() = runTest { - coEvery { enrolmentRecordRepository.load(any()) } returns listOf(subject) + coEvery { enrolmentRecordRepository.load(any()) } returns listOf(enrolmentRecord) val metadata = "ABC" val result = useCase(DEFAULT_PROJECT_ID, DEFAULT_SUBJECT_ID, metadata) @@ -71,11 +71,11 @@ internal class FetchSubjectUseCaseTest { @Test fun `fetch should return from local DB if present after downsync`() = runTest { - coEvery { enrolmentRecordRepository.load(any()) } returnsMany listOf(emptyList(), listOf(subject)) + coEvery { enrolmentRecordRepository.load(any()) } returnsMany listOf(emptyList(), listOf(enrolmentRecord)) val result = useCase(DEFAULT_PROJECT_ID, DEFAULT_SUBJECT_ID, "") - coVerify(exactly = 2) { enrolmentRecordRepository.load(SubjectQuery(DEFAULT_PROJECT_ID, DEFAULT_SUBJECT_ID)) } + coVerify(exactly = 2) { enrolmentRecordRepository.load(EnrolmentRecordQuery(DEFAULT_PROJECT_ID, DEFAULT_SUBJECT_ID)) } assertThat(result).isInstanceOf(FetchSubjectState.FoundRemote::class.java) } diff --git a/feature/matcher/src/main/java/com/simprints/matcher/MatchContract.kt b/feature/matcher/src/main/java/com/simprints/matcher/MatchContract.kt index 8ded5aeb1b..f6c109c624 100644 --- a/feature/matcher/src/main/java/com/simprints/matcher/MatchContract.kt +++ b/feature/matcher/src/main/java/com/simprints/matcher/MatchContract.kt @@ -2,27 +2,25 @@ package com.simprints.matcher import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.ModalitySdkType -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.matching.MatchParams object MatchContract { val DESTINATION = R.id.matcherFragment fun getParams( - referenceId: String = "", - probeSamples: List = emptyList(), + probeReference: BiometricReferenceCapture, bioSdk: ModalitySdkType, flowType: FlowType, - subjectQuery: SubjectQuery, + enrolmentRecordQuery: EnrolmentRecordQuery, biometricDataSource: BiometricDataSource, ) = MatchParams( - probeReferenceId = referenceId, bioSdk = bioSdk, - probeSamples = probeSamples, + probeReference = probeReference, flowType = flowType, - queryForCandidates = subjectQuery, + queryForCandidates = enrolmentRecordQuery, biometricDataSource = biometricDataSource, ) } diff --git a/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchViewModel.kt b/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchViewModel.kt index 71c77c06ab..8bd0f026ac 100644 --- a/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchViewModel.kt +++ b/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchViewModel.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.livedata.LiveDataEventWithContent import com.simprints.core.livedata.send import com.simprints.core.tools.time.TimeHelper @@ -106,14 +106,14 @@ internal class MatchViewModel @Inject constructor( private fun setMatchState( candidatesMatched: Int, - results: List, + results: List, decisionPolicy: DecisionPolicy, ) { - val veryGoodMatches = results.count { decisionPolicy.high <= it.confidence } + val veryGoodMatches = results.count { decisionPolicy.high <= it.comparisonScore } val goodMatches = - results.count { decisionPolicy.medium <= it.confidence && it.confidence < decisionPolicy.high } + results.count { decisionPolicy.medium <= it.comparisonScore && it.comparisonScore < decisionPolicy.high } val fairMatches = - results.count { decisionPolicy.low <= it.confidence && it.confidence < decisionPolicy.medium } + results.count { decisionPolicy.low <= it.comparisonScore && it.comparisonScore < decisionPolicy.medium } _matchState.postValue( MatchState.Finished( diff --git a/feature/matcher/src/test/java/com/simprints/matcher/screen/MatchViewModelTest.kt b/feature/matcher/src/test/java/com/simprints/matcher/screen/MatchViewModelTest.kt index 9fa8cde69e..fce84a0124 100644 --- a/feature/matcher/src/test/java/com/simprints/matcher/screen/MatchViewModelTest.kt +++ b/feature/matcher/src/test/java/com/simprints/matcher/screen/MatchViewModelTest.kt @@ -3,11 +3,12 @@ package com.simprints.matcher.screen import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.google.common.truth.Truth.* import com.jraska.livedata.test +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.MatchComparisonResult -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.infra.config.store.models.DecisionPolicy @@ -82,7 +83,7 @@ internal class MatchViewModelTest { @Test fun `when setup is called, then view model becomes initialized`() = runTest { val responseItems = listOf( - MatchComparisonResult("1", 90f), + ComparisonResult("1", 90f), ) coEvery { faceMatcherUseCase.invoke(any(), any()) } returns flow { @@ -104,8 +105,12 @@ internal class MatchViewModelTest { viewModel.matchState.test() viewModel.setupMatch( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf(getFaceSample()), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "ROCv1", + templates = listOf(getFaceCapture()), + ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, flowType = FlowType.ENROL, queryForCandidates = mockk {}, @@ -134,13 +139,13 @@ internal class MatchViewModelTest { } returns DecisionPolicy(20, 35, 50) val responseItems = listOf( - MatchComparisonResult("1", 90f), - MatchComparisonResult("1", 80f), - MatchComparisonResult("1", 55f), - MatchComparisonResult("1", 40f), - MatchComparisonResult("1", 36f), - MatchComparisonResult("1", 20f), - MatchComparisonResult("1", 10f), + ComparisonResult("1", 90f), + ComparisonResult("1", 80f), + ComparisonResult("1", 55f), + ComparisonResult("1", 40f), + ComparisonResult("1", 36f), + ComparisonResult("1", 20f), + ComparisonResult("1", 10f), ) val batches = listOf( MatchBatchInfo( @@ -176,8 +181,12 @@ internal class MatchViewModelTest { val states = viewModel.matchState.test() viewModel.setupMatch( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf(getFaceSample()), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "ROCv1", + templates = listOf(getFaceCapture()), + ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, flowType = FlowType.ENROL, queryForCandidates = mockk {}, @@ -223,13 +232,13 @@ internal class MatchViewModelTest { } returns DecisionPolicy(200, 350, 500) val responseItems = listOf( - MatchComparisonResult("1", 900f), - MatchComparisonResult("1", 800f), - MatchComparisonResult("1", 550f), - MatchComparisonResult("1", 400f), - MatchComparisonResult("1", 360f), - MatchComparisonResult("1", 200f), - MatchComparisonResult("1", 100f), + ComparisonResult("1", 900f), + ComparisonResult("1", 800f), + ComparisonResult("1", 550f), + ComparisonResult("1", 400f), + ComparisonResult("1", 360f), + ComparisonResult("1", 200f), + ComparisonResult("1", 100f), ) val batches = listOf( MatchBatchInfo( @@ -267,8 +276,12 @@ internal class MatchViewModelTest { viewModel.setupMatch( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf(getFingerprintSample()), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "Secugen", + templates = listOf(getFingerprintTemplate()), + ), bioSdk = SECUGEN_SIM_MATCHER, flowType = FlowType.ENROL, queryForCandidates = mockk {}, @@ -314,8 +327,8 @@ internal class MatchViewModelTest { } returns null val responseItems = listOf( - MatchComparisonResult("1", 90f), - MatchComparisonResult("1", 10f), + ComparisonResult("1", 90f), + ComparisonResult("1", 10f), ) coEvery { faceMatcherUseCase.invoke(any(), any()) } returns flow { emit(MatcherUseCase.MatcherState.LoadingStarted(responseItems.size)) @@ -332,8 +345,12 @@ internal class MatchViewModelTest { val states = viewModel.matchState.test() viewModel.setupMatch( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf(getFaceSample()), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "ROC", + templates = listOf(getFaceCapture()), + ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, flowType = FlowType.ENROL, queryForCandidates = mockk {}, @@ -360,7 +377,7 @@ internal class MatchViewModelTest { emit(MatcherUseCase.MatcherState.CandidateLoaded) emit( MatcherUseCase.MatcherState.Success( - comparisonResults = listOf(MatchComparisonResult("1", 90f)), + comparisonResults = listOf(ComparisonResult("1", 90f)), totalCandidates = 1, matcherName = MATCHER_NAME, matchBatches = emptyList(), @@ -371,8 +388,12 @@ internal class MatchViewModelTest { val states = viewModel.matchState.test() val matchParams = MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf(getFaceSample()), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "ROC", + templates = listOf(getFaceCapture()), + ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, flowType = FlowType.ENROL, queryForCandidates = mockk {}, @@ -391,19 +412,15 @@ internal class MatchViewModelTest { assertThat(states.valueHistory()).hasSize(4) } - private fun getFaceSample(): CaptureSample = CaptureSample( + private fun getFaceCapture(): BiometricTemplateCapture = BiometricTemplateCapture( captureEventId = UUID.randomUUID().toString(), - modality = Modality.FACE, template = Random.nextBytes(20), - format = "format", ) - private fun getFingerprintSample(): CaptureSample = CaptureSample( + private fun getFingerprintTemplate(): BiometricTemplateCapture = BiometricTemplateCapture( captureEventId = UUID.randomUUID().toString(), - modality = Modality.FINGERPRINT, template = Random.nextBytes(20), - format = "format", - identifier = SampleIdentifier.LEFT_3RD_FINGER, + identifier = TemplateIdentifier.LEFT_3RD_FINGER, ) companion object { diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt index 291f9ae6dd..fb7d686807 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt @@ -8,8 +8,8 @@ import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.module.SimpleModule import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.response.AppErrorReason -import com.simprints.core.domain.sample.CaptureIdentity import com.simprints.core.domain.step.StepResult import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameDeserializer @@ -256,7 +256,7 @@ internal class OrchestratorViewModel @Inject constructor( currentStep: Step, result: Serializable, ) { - if (currentStep.id == StepId.FACE_CAPTURE && result is CaptureIdentity) { + if (currentStep.id == StepId.FACE_CAPTURE && result is BiometricReferenceCapture) { val captureParams = currentStep.params?.let { it as? FaceCaptureParams } val matchingStep = steps.firstOrNull { step -> if (step.id != StepId.FACE_MATCHER) { @@ -270,14 +270,14 @@ internal class OrchestratorViewModel @Inject constructor( if (matchingStep != null) { val newPayload = matchingStep.params ?.let { it as? MatchStepStubPayload } - ?.toFaceStepArgs(result.referenceId, result.samples) + ?.toFaceStepArgs(result) if (newPayload != null) { matchingStep.params = newPayload } } } - if (currentStep.id == StepId.FINGERPRINT_CAPTURE && result is CaptureIdentity) { + if (currentStep.id == StepId.FINGERPRINT_CAPTURE && result is BiometricReferenceCapture) { val captureParams = currentStep.params?.let { it as? FingerprintCaptureParams } // Find the matching step for the same fingerprint SDK as there may be multiple match steps val matchingStep = steps.firstOrNull { step -> @@ -292,7 +292,7 @@ internal class OrchestratorViewModel @Inject constructor( if (matchingStep != null) { val newPayload = matchingStep.params ?.let { it as? MatchStepStubPayload } - ?.toFingerprintStepArgs(result.referenceId, result.samples) + ?.toFingerprintStepArgs(result) if (newPayload != null) { matchingStep.params = newPayload @@ -309,26 +309,11 @@ internal class OrchestratorViewModel @Inject constructor( result: StepResult, ) { if (currentStep.id !in listOf(StepId.FACE_CAPTURE, StepId.FINGERPRINT_CAPTURE)) return + if (result !is BiometricReferenceCapture) return + val step = steps.firstOrNull { it.id == StepId.EXTERNAL_CREDENTIAL } ?: return val params = step.params as? ExternalCredentialParams ?: return - val updatedParams = when { - currentStep.id == StepId.FACE_CAPTURE && result is CaptureIdentity -> { - params.copy( - probeReferenceId = result.referenceId, - samples = params.samples + (Modality.FACE to result.samples), - ) - } - - currentStep.id == StepId.FINGERPRINT_CAPTURE && result is CaptureIdentity -> { - params.copy( - probeReferenceId = result.referenceId, - samples = params.samples + (Modality.FINGERPRINT to result.samples), - ) - } - - else -> params - } - step.params = updatedParams + step.params = params.copy(probeReferences = params.probeReferences + listOf(result)) } fun setActionRequestFromJson(json: String) { diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt index 2698aa32f3..d15b4679c0 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt @@ -2,11 +2,10 @@ package com.simprints.feature.orchestrator.steps import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.ModalitySdkType -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.step.StepParams import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery -import com.simprints.infra.matching.MatchParams +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.matcher.MatchContract /** @@ -17,31 +16,23 @@ import com.simprints.matcher.MatchContract */ internal data class MatchStepStubPayload( val flowType: FlowType, - val subjectQuery: SubjectQuery, + val enrolmentRecordQuery: EnrolmentRecordQuery, val biometricDataSource: BiometricDataSource, val bioSdk: ModalitySdkType, ) : StepParams { - fun toFaceStepArgs( - referenceId: String, - samples: List, - ) = MatchContract.getParams( - referenceId = referenceId, - probeSamples = samples, + fun toFaceStepArgs(probeReference: BiometricReferenceCapture) = MatchContract.getParams( + probeReference = probeReference, bioSdk = bioSdk, flowType = flowType, - subjectQuery = subjectQuery, + enrolmentRecordQuery = enrolmentRecordQuery, biometricDataSource = biometricDataSource, ) - fun toFingerprintStepArgs( - referenceId: String, - samples: List, - ) = MatchContract.getParams( - referenceId = referenceId, - probeSamples = samples, + fun toFingerprintStepArgs(probeReference: BiometricReferenceCapture) = MatchContract.getParams( + probeReference = probeReference, bioSdk = bioSdk, flowType = flowType, - subjectQuery = subjectQuery, + enrolmentRecordQuery = enrolmentRecordQuery, biometricDataSource = biometricDataSource, ) @@ -50,9 +41,9 @@ internal data class MatchStepStubPayload( fun getMatchStubParams( flowType: FlowType, - subjectQuery: SubjectQuery, + enrolmentRecordQuery: EnrolmentRecordQuery, biometricDataSource: BiometricDataSource, bioSdk: ModalitySdkType, - ) = MatchStepStubPayload(flowType, subjectQuery, biometricDataSource, bioSdk) + ) = MatchStepStubPayload(flowType, enrolmentRecordQuery, biometricDataSource, bioSdk) } } diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt index 3051833d3e..c16b0ba0be 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt @@ -4,10 +4,11 @@ import androidx.annotation.IdRes import androidx.annotation.Keep import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture import com.simprints.core.domain.common.AgeGroup -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.step.StepResult import com.simprints.face.capture.FaceCaptureParams @@ -37,7 +38,7 @@ import com.simprints.fingerprint.connect.FingerprintConnectResult import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.matching.MatchParams import com.simprints.infra.matching.MatchResult import java.io.Serializable @@ -57,11 +58,12 @@ import java.io.Serializable JsonSubTypes.Type(value = ValidateSubjectPoolResult::class, name = "ValidateSubjectPoolResult"), JsonSubTypes.Type(value = SelectSubjectAgeGroupResult::class, name = "SelectSubjectAgeGroupResult"), JsonSubTypes.Type(value = ExternalCredentialSearchResult::class, name = "ExternalCredentialSearchResult"), - JsonSubTypes.Type(value = CredentialMatch::class, name = " CredentialMatch"), + JsonSubTypes.Type(value = CredentialMatch::class, name = "CredentialMatch"), // Common data types - JsonSubTypes.Type(value = CaptureIdentity::class, name = "CaptureIdentity"), - JsonSubTypes.Type(value = CaptureSample::class, name = "CaptureSample"), - JsonSubTypes.Type(value = MatchComparisonResult::class, name = "MatchComparisonResult"), + JsonSubTypes.Type(value = BiometricReferenceCapture::class, name = "BiometricReferenceCapture"), + JsonSubTypes.Type(value = BiometricTemplateCapture::class, name = "BiometricTemplateCapture"), + JsonSubTypes.Type(value = BiometricTemplate::class, name = "BiometricTemplate"), + JsonSubTypes.Type(value = ComparisonResult::class, name = "ComparisonResult"), ) abstract class StepResultMixin : StepResult @@ -95,12 +97,14 @@ abstract class StepResultMixin : StepResult ), JsonSubTypes.Type(value = ExternalCredentialParams::class, name = "ExternalCredentialParams"), // Additional types that are used in top-level params - JsonSubTypes.Type(value = CaptureSample::class, name = "CaptureSample"), - JsonSubTypes.Type(value = MatchComparisonResult::class, name = "MatchComparisonResult"), + JsonSubTypes.Type(value = BiometricReferenceCapture::class, name = "BiometricReferenceCapture"), + JsonSubTypes.Type(value = BiometricTemplateCapture::class, name = "BiometricTemplateCapture"), + JsonSubTypes.Type(value = BiometricTemplate::class, name = "BiometricTemplate"), + JsonSubTypes.Type(value = ComparisonResult::class, name = "ComparisonResult"), JsonSubTypes.Type(value = BiometricDataSource::class, name = "BiometricDataSource"), JsonSubTypes.Type(value = BiometricDataSource.CommCare::class, name = "BiometricDataSource.CommCare"), JsonSubTypes.Type(value = BiometricDataSource.Simprints::class, name = "BiometricDataSource.Simprints"), - JsonSubTypes.Type(value = SubjectQuery::class, name = "SubjectQuery"), + JsonSubTypes.Type(value = EnrolmentRecordQuery::class, name = "EnrolmentRecordQuery"), JsonSubTypes.Type(value = AgeGroup::class, name = "AgeGroup"), JsonSubTypes.Type(value = FingerprintConfiguration.BioSdk::class, name = "FingerprintConfiguration.BioSdk"), JsonSubTypes.Type(value = FaceConfiguration.BioSdk::class, name = "FaceConfiguration.BioSdk"), diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCase.kt index 986164d220..6f9f18b8af 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapStepsForLastBiometricEnrolUseCase.kt @@ -1,7 +1,7 @@ package com.simprints.feature.orchestrator.usecases -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.feature.enrollast.EnrolLastBiometricResult import com.simprints.feature.enrollast.EnrolLastBiometricStepResult import com.simprints.infra.matching.MatchResult @@ -12,17 +12,12 @@ import javax.inject.Inject internal class MapStepsForLastBiometricEnrolUseCase @Inject constructor() { operator fun invoke(results: List) = results.mapNotNull { result -> when (result) { - is EnrolLastBiometricResult -> EnrolLastBiometricStepResult.EnrolLastBiometricsResult( - result.newSubjectId, - ) + is EnrolLastBiometricResult -> EnrolLastBiometricStepResult.EnrolLastBiometricsResult(result.newSubjectId) - is CaptureIdentity -> EnrolLastBiometricStepResult.CaptureResult( - result.referenceId, - result.samples, - ) + is BiometricReferenceCapture -> EnrolLastBiometricStepResult.CaptureResult(result) is MatchResult -> EnrolLastBiometricStepResult.MatchResult( - result.results.map { MatchComparisonResult(it.subjectId, it.confidence) }, + result.results.map { ComparisonResult(it.subjectId, it.comparisonScore) }, result.sdk, ) diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCase.kt index 7ec8ae7237..fe5954d9f3 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCase.kt @@ -1,11 +1,11 @@ package com.simprints.feature.orchestrator.usecases.response +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.response.AppErrorReason -import com.simprints.core.domain.sample.CaptureIdentity import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.screens.search.model.toExternalCredential import com.simprints.infra.config.store.models.Project -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber import com.simprints.infra.orchestration.data.ActionRequest @@ -16,8 +16,8 @@ import java.io.Serializable import javax.inject.Inject internal class CreateEnrolResponseUseCase @Inject constructor( - private val subjectFactory: SubjectFactory, - private val enrolSubject: EnrolSubjectUseCase, + private val enrolmentRecordFactory: EnrolmentRecordFactory, + private val enrolRecord: EnrolRecordUseCase, ) { suspend operator fun invoke( request: ActionRequest.EnrolActionRequest, @@ -29,17 +29,17 @@ internal class CreateEnrolResponseUseCase @Inject constructor( val externalCredential = credentialResult?.scannedCredential?.toExternalCredential(enrolmentSubjectId) return try { - val subject = subjectFactory.buildSubjectFromCaptureResults( + val record = enrolmentRecordFactory.buildFromCaptureResults( subjectId = enrolmentSubjectId, projectId = request.projectId, attendantId = request.userId, moduleId = request.moduleId, - captures = results.filterIsInstance(), + captures = results.filterIsInstance(), externalCredential = externalCredential, ) - enrolSubject(subject, project) + enrolRecord(record, project) - AppEnrolResponse(subject.subjectId, externalCredential) + AppEnrolResponse(record.subjectId, externalCredential) } catch (e: Exception) { Simber.e("Error creating enrol response", e, tag = ORCHESTRATION) AppErrorResponse(AppErrorReason.UNEXPECTED_ERROR) diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCase.kt index 6170bdf803..4c6c0ad621 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCase.kt @@ -70,9 +70,9 @@ internal class CreateIdentifyResponseUseCase @Inject constructor( credentialSearchResult.matchResults.mapNotNull { credentialMatchResult -> val sdk = credentialMatchResult.bioSdk val policy = projectConfiguration.getModalitySdkConfig(sdk)?.decisionPolicy ?: return@mapNotNull null - val matchResult = credentialMatchResult.matchResult + val matchResult = credentialMatchResult.comparisonResult - sdk to AppMatchResult(matchResult.subjectId, matchResult.confidence, policy, true) + sdk to AppMatchResult(matchResult.subjectId, matchResult.comparisonScore, policy, true) } }.groupDescendingResultsBySdk() @@ -88,7 +88,7 @@ internal class CreateIdentifyResponseUseCase @Inject constructor( ?: return@flatMap emptyList() matchResult.results.map { - matchResult.sdk to AppMatchResult(it.subjectId, it.confidence, policy, false) + matchResult.sdk to AppMatchResult(it.subjectId, it.comparisonScore, policy, false) } }.groupDescendingResultsBySdk() diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCase.kt index ba6bc1ea25..cdf32e0549 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCase.kt @@ -32,10 +32,10 @@ internal class CreateVerifyResponseUseCase @Inject constructor() { .filterIsInstance() .mapNotNull { matchResult -> projectConfiguration.getModalitySdkConfig(matchResult.sdk)?.let { sdkConfiguration -> - matchResult.results.maxByOrNull { it.confidence }?.let { + matchResult.results.maxByOrNull { it.comparisonScore }?.let { AppMatchResult( guid = it.subjectId, - confidenceScore = it.confidence, + confidenceScore = it.comparisonScore, decisionPolicy = sdkConfiguration.decisionPolicy, verificationMatchThreshold = sdkConfiguration.verificationMatchThreshold, isCredentialMatch = false, diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/EnrolSubjectUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/EnrolRecordUseCase.kt similarity index 77% rename from feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/EnrolSubjectUseCase.kt rename to feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/EnrolRecordUseCase.kt index 9d7a7c6fdc..8fc099c1ea 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/EnrolSubjectUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/EnrolRecordUseCase.kt @@ -3,21 +3,21 @@ package com.simprints.feature.orchestrator.usecases.response import com.simprints.core.tools.time.TimeHelper import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.events.event.domain.models.BiometricReferenceCreationEvent import com.simprints.infra.events.event.domain.models.EnrolmentEventV4 import com.simprints.infra.events.event.domain.models.ExternalCredentialCaptureValueEvent import com.simprints.infra.events.session.SessionEventRepository import javax.inject.Inject -internal class EnrolSubjectUseCase @Inject constructor( +internal class EnrolRecordUseCase @Inject constructor( private val eventRepository: SessionEventRepository, private val timeHelper: TimeHelper, private val enrolmentRecordRepository: EnrolmentRecordRepository, ) { suspend operator fun invoke( - subject: Subject, + enrolmentRecord: EnrolmentRecord, project: Project, ) { val events = eventRepository @@ -35,14 +35,14 @@ internal class EnrolSubjectUseCase @Inject constructor( eventRepository.addOrUpdateEvent( EnrolmentEventV4( createdAt = timeHelper.now(), - subjectId = subject.subjectId, - projectId = subject.projectId, - moduleId = subject.moduleId, - attendantId = subject.attendantId, + subjectId = enrolmentRecord.subjectId, + projectId = enrolmentRecord.projectId, + moduleId = enrolmentRecord.moduleId, + attendantId = enrolmentRecord.attendantId, biometricReferenceIds = biometricReferenceIds, externalCredentialIds = externalCredentialIds, ), ) - enrolmentRecordRepository.performActions(listOf(SubjectAction.Creation(subject)), project) + enrolmentRecordRepository.performActions(listOf(EnrolmentRecordAction.Creation(enrolmentRecord)), project) } } diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCase.kt index 674f501ff9..615b977d02 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCase.kt @@ -48,5 +48,5 @@ internal class IsNewEnrolmentUseCase @Inject constructor() { ?.decisionPolicy ?.medium ?.toFloat() - ?.let { threshold -> matchResult.results.isNotEmpty() && matchResult.results.all { it.confidence < threshold } } != false + ?.let { threshold -> matchResult.results.isNotEmpty() && matchResult.results.all { it.comparisonScore < threshold } } != false } diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildMatcherSubjectQueryUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildMatcherSubjectQueryUseCase.kt index c695d7d979..f3f11c5683 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildMatcherSubjectQueryUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildMatcherSubjectQueryUseCase.kt @@ -3,7 +3,7 @@ package com.simprints.feature.orchestrator.usecases.steps import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import com.simprints.infra.config.store.models.IdentificationConfiguration import com.simprints.infra.config.store.models.ProjectConfiguration -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.orchestration.data.ActionRequest import javax.inject.Inject @@ -13,18 +13,18 @@ internal class BuildMatcherSubjectQueryUseCase @Inject constructor() { projectConfiguration: ProjectConfiguration, actionRequest: ActionRequest, ) = when (projectConfiguration.identification.poolType) { - IdentificationConfiguration.PoolType.PROJECT -> SubjectQuery( + IdentificationConfiguration.PoolType.PROJECT -> EnrolmentRecordQuery( projectId = actionRequest.projectId, metadata = actionRequest.metadata, ) - IdentificationConfiguration.PoolType.USER -> SubjectQuery( + IdentificationConfiguration.PoolType.USER -> EnrolmentRecordQuery( projectId = actionRequest.projectId, attendantId = actionRequest.userId, metadata = actionRequest.metadata, ) - IdentificationConfiguration.PoolType.MODULE -> SubjectQuery( + IdentificationConfiguration.PoolType.MODULE -> EnrolmentRecordQuery( projectId = actionRequest.projectId, moduleId = (actionRequest as ActionRequest.FlowAction).moduleId, metadata = actionRequest.metadata, diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt index bf21988de4..8fd90bedc6 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt @@ -31,7 +31,7 @@ import com.simprints.infra.config.store.models.getSdkListForAgeGroup import com.simprints.infra.config.store.models.isAgeRestricted import com.simprints.infra.config.store.models.sortedUniqueAgeGroups import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.orchestration.data.ActionRequest import com.simprints.matcher.MatchContract import javax.inject.Inject @@ -48,12 +48,14 @@ internal class BuildStepsUseCase @Inject constructor( enrolmentSubjectId: String, cachedScannedCredential: ScannedCredential?, ) = when (action) { - is ActionRequest.EnrolActionRequest -> listOf( - buildSetupStep(), - buildAgeSelectionStepIfNeeded(action, projectConfiguration), - buildConsentStepIfNeeded(ConsentType.ENROL, projectConfiguration), - buildCaptureAndMatchStepsForEnrol(action, projectConfiguration, enrolmentSubjectId = enrolmentSubjectId), - ) + is ActionRequest.EnrolActionRequest -> { + listOf( + buildSetupStep(), + buildAgeSelectionStepIfNeeded(action, projectConfiguration), + buildConsentStepIfNeeded(ConsentType.ENROL, projectConfiguration), + buildCaptureAndMatchStepsForEnrol(action, projectConfiguration, enrolmentSubjectId = enrolmentSubjectId), + ) + } is ActionRequest.IdentifyActionRequest -> { val subjectQuery = buildMatcherSubjectQuery(projectConfiguration, action) @@ -61,7 +63,7 @@ internal class BuildStepsUseCase @Inject constructor( listOf( buildSetupStep(), buildValidateIdPoolStep( - subjectQuery = subjectQuery, + enrolmentRecordQuery = subjectQuery, biometricDataSource = action.biometricDataSource, callerPackageName = action.actionIdentifier.callerPackageName, projectConfiguration = projectConfiguration, @@ -71,33 +73,39 @@ internal class BuildStepsUseCase @Inject constructor( buildCaptureAndMatchStepsForIdentify( action = action, projectConfiguration = projectConfiguration, - subjectQuery = subjectQuery, + enrolmentRecordQuery = subjectQuery, enrolmentSubjectId = enrolmentSubjectId, ), ) } - is ActionRequest.VerifyActionRequest -> listOf( - buildSetupStep(), - buildAgeSelectionStepIfNeeded(action, projectConfiguration), - buildFetchGuidStepIfNeeded( - projectId = action.projectId, - subjectId = action.verifyGuid, - biometricDataSource = action.biometricDataSource, - callerPackageName = action.actionIdentifier.callerPackageName, - metadata = action.metadata, - ), - buildConsentStepIfNeeded(ConsentType.VERIFY, projectConfiguration), - buildCaptureAndMatchStepsForVerify(action, projectConfiguration), - ) + is ActionRequest.VerifyActionRequest -> { + listOf( + buildSetupStep(), + buildAgeSelectionStepIfNeeded(action, projectConfiguration), + buildFetchGuidStepIfNeeded( + projectId = action.projectId, + subjectId = action.verifyGuid, + biometricDataSource = action.biometricDataSource, + callerPackageName = action.actionIdentifier.callerPackageName, + metadata = action.metadata, + ), + buildConsentStepIfNeeded(ConsentType.VERIFY, projectConfiguration), + buildCaptureAndMatchStepsForVerify(action, projectConfiguration), + ) + } - is ActionRequest.EnrolLastBiometricActionRequest -> listOf( - buildEnrolLastBiometricStep(action, projectConfiguration, cachedScannedCredential), - ) + is ActionRequest.EnrolLastBiometricActionRequest -> { + listOf( + buildEnrolLastBiometricStep(action, projectConfiguration, cachedScannedCredential), + ) + } - is ActionRequest.ConfirmIdentityActionRequest -> listOf( - buildConfirmIdentityStep(action, cachedScannedCredential), - ) + is ActionRequest.ConfirmIdentityActionRequest -> { + listOf( + buildConfirmIdentityStep(action, cachedScannedCredential), + ) + } }.flatten() suspend fun buildCaptureAndMatchStepsForAgeGroup( @@ -117,7 +125,7 @@ internal class BuildStepsUseCase @Inject constructor( action = action, projectConfiguration = projectConfiguration, ageGroup = ageGroup, - subjectQuery = buildMatcherSubjectQuery(projectConfiguration, action), + enrolmentRecordQuery = buildMatcherSubjectQuery(projectConfiguration, action), enrolmentSubjectId = enrolmentSubjectId, ) @@ -155,7 +163,9 @@ internal class BuildStepsUseCase @Inject constructor( ) } - FlowType.VERIFY -> emptyList() + FlowType.VERIFY -> { + emptyList() + } } } @@ -175,6 +185,7 @@ internal class BuildStepsUseCase @Inject constructor( ) val externalCredentialStep = when { captureSteps.isEmpty() -> emptyList() + else -> buildExternalCredentialStepIfNeeded( ageGroup = ageGroup, enrolmentSubjectId = enrolmentSubjectId, @@ -203,7 +214,7 @@ internal class BuildStepsUseCase @Inject constructor( action: ActionRequest.IdentifyActionRequest, projectConfiguration: ProjectConfiguration, ageGroup: AgeGroup? = null, - subjectQuery: SubjectQuery, + enrolmentRecordQuery: EnrolmentRecordQuery, enrolmentSubjectId: String, ): List { val action = fallbackToCommCareDataSourceIfNeeded(action, projectConfiguration) @@ -216,6 +227,7 @@ internal class BuildStepsUseCase @Inject constructor( ) val externalCredentialStep = when { captureSteps.isEmpty() -> emptyList() + else -> buildExternalCredentialStepIfNeeded( ageGroup = ageGroup, enrolmentSubjectId = enrolmentSubjectId, @@ -227,7 +239,7 @@ internal class BuildStepsUseCase @Inject constructor( projectConfiguration, identifyFlowType, resolvedAgeGroup, - subjectQuery, + enrolmentRecordQuery, BiometricDataSource.fromString( action.biometricDataSource, action.actionIdentifier.callerPackageName, @@ -253,7 +265,7 @@ internal class BuildStepsUseCase @Inject constructor( projectConfiguration, FlowType.VERIFY, resolvedAgeGroup, - SubjectQuery(subjectId = action.verifyGuid, metadata = action.metadata), + EnrolmentRecordQuery(subjectId = action.verifyGuid, metadata = action.metadata), BiometricDataSource.fromString( action.biometricDataSource, action.actionIdentifier.callerPackageName, @@ -333,7 +345,7 @@ internal class BuildStepsUseCase @Inject constructor( } private fun buildValidateIdPoolStep( - subjectQuery: SubjectQuery, + enrolmentRecordQuery: EnrolmentRecordQuery, biometricDataSource: String, callerPackageName: String, projectConfiguration: ProjectConfiguration, @@ -349,7 +361,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.VALIDATE_ID_POOL, navigationActionId = R.id.action_orchestratorFragment_to_validateSubjectPool, destinationId = ValidateSubjectPoolContract.DESTINATION, - params = ValidateSubjectPoolContract.getParams(subjectQuery), + params = ValidateSubjectPoolContract.getParams(enrolmentRecordQuery), ), ) @@ -431,7 +443,9 @@ internal class BuildStepsUseCase @Inject constructor( ) } - else -> null + else -> { + null + } } } @@ -445,14 +459,14 @@ internal class BuildStepsUseCase @Inject constructor( projectConfiguration: ProjectConfiguration, flowType: FlowType, ageGroup: AgeGroup?, - subjectQuery: SubjectQuery, + enrolmentRecordQuery: EnrolmentRecordQuery, biometricDataSource: BiometricDataSource, ): List = projectConfiguration.general.matchingModalities .flatMap { modality -> - buildMatcherStepsForModality(modality, projectConfiguration, ageGroup, flowType, subjectQuery, biometricDataSource) + buildMatcherStepsForModality(modality, projectConfiguration, ageGroup, flowType, enrolmentRecordQuery, biometricDataSource) }.ifEmpty { projectConfiguration.general.modalities.flatMap { modality -> - buildMatcherStepsForModality(modality, projectConfiguration, ageGroup, flowType, subjectQuery, biometricDataSource) + buildMatcherStepsForModality(modality, projectConfiguration, ageGroup, flowType, enrolmentRecordQuery, biometricDataSource) } } @@ -461,12 +475,12 @@ internal class BuildStepsUseCase @Inject constructor( projectConfiguration: ProjectConfiguration, ageGroup: AgeGroup?, flowType: FlowType, - subjectQuery: SubjectQuery, + enrolmentRecordQuery: EnrolmentRecordQuery, biometricDataSource: BiometricDataSource, ): List = projectConfiguration.getSdkListForAgeGroup(modality, ageGroup).mapNotNull { bioSDK -> val paramStub = MatchStepStubPayload.getMatchStubParams( flowType = flowType, - subjectQuery = subjectQuery, + enrolmentRecordQuery = enrolmentRecordQuery, biometricDataSource = biometricDataSource, bioSdk = bioSDK, ) diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt index fcf17fc263..b7fad3ba1a 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt @@ -7,10 +7,10 @@ import com.jraska.livedata.test import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.response.AppErrorReason -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.SampleIdentifier import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.feature.consent.ConsentResult @@ -40,7 +40,7 @@ import com.simprints.infra.config.store.models.FingerprintConfiguration.BioSdk.N import com.simprints.infra.config.store.models.FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 import com.simprints.infra.events.sampledata.SampleDefaults.GUID2 import com.simprints.infra.matching.MatchParams @@ -195,7 +195,7 @@ internal class OrchestratorViewModelTest { StepId.FACE_MATCHER, MatchStepStubPayload.getMatchStubParams( FlowType.VERIFY, - SubjectQuery(), + EnrolmentRecordQuery(), BiometricDataSource.Simprints, FaceConfiguration.BioSdk.RANK_ONE, ), @@ -220,7 +220,7 @@ internal class OrchestratorViewModelTest { StepId.FACE_MATCHER, MatchStepStubPayload.getMatchStubParams( FlowType.VERIFY, - SubjectQuery(), + EnrolmentRecordQuery(), BiometricDataSource.Simprints, FaceConfiguration.BioSdk.RANK_ONE, ), @@ -229,7 +229,7 @@ internal class OrchestratorViewModelTest { coEvery { mapRefusalOrErrorResult(any(), any()) } returns null viewModel.handleAction(mockk()) - viewModel.handleResult(CaptureIdentity("", Modality.FACE, emptyList())) + viewModel.handleResult(BiometricReferenceCapture("", Modality.FACE, "format", emptyList())) viewModel.currentStep.test().value().peekContent()?.let { step -> assertThat(step.id).isEqualTo(StepId.FACE_MATCHER) @@ -244,7 +244,7 @@ internal class OrchestratorViewModelTest { StepId.FINGERPRINT_MATCHER, MatchStepStubPayload.getMatchStubParams( FlowType.VERIFY, - SubjectQuery(), + EnrolmentRecordQuery(), BiometricDataSource.Simprints, FaceConfiguration.BioSdk.RANK_ONE, ), @@ -253,7 +253,7 @@ internal class OrchestratorViewModelTest { coEvery { mapRefusalOrErrorResult(any(), any()) } returns null viewModel.handleAction(mockk()) - viewModel.handleResult(CaptureIdentity("", Modality.FINGERPRINT, emptyList())) + viewModel.handleResult(BiometricReferenceCapture("", Modality.FINGERPRINT, "format", emptyList())) viewModel.currentStep.test().value().peekContent()?.let { step -> assertThat(step.id).isEqualTo(StepId.FINGERPRINT_MATCHER) @@ -275,7 +275,7 @@ internal class OrchestratorViewModelTest { StepId.FINGERPRINT_MATCHER, MatchStepStubPayload.getMatchStubParams( flowType = FlowType.VERIFY, - subjectQuery = SubjectQuery(), + enrolmentRecordQuery = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, bioSdk = SECUGEN_SIM_MATCHER, ), @@ -292,7 +292,7 @@ internal class OrchestratorViewModelTest { StepId.FINGERPRINT_MATCHER, MatchStepStubPayload.getMatchStubParams( flowType = FlowType.VERIFY, - subjectQuery = SubjectQuery(), + enrolmentRecordQuery = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, bioSdk = NEC, ), @@ -300,32 +300,27 @@ internal class OrchestratorViewModelTest { ) coEvery { mapRefusalOrErrorResult(any(), any()) } returns null val format = "SimMatcher" - val sample1 = CaptureSample( + val capture1 = BiometricTemplateCapture( captureEventId = GUID1, - modality = Modality.FINGERPRINT, - identifier = SampleIdentifier.LEFT_INDEX_FINGER, + identifier = TemplateIdentifier.LEFT_INDEX_FINGER, template = ByteArray(0), - format = format, ) - val sample2 = CaptureSample( + val capture2 = BiometricTemplateCapture( captureEventId = GUID2, - modality = Modality.FINGERPRINT, - identifier = SampleIdentifier.LEFT_THUMB, + identifier = TemplateIdentifier.LEFT_THUMB, template = ByteArray(0), - format = format, ) viewModel.handleAction(mockk()) - viewModel.handleResult(CaptureIdentity("", Modality.FINGERPRINT, listOf(sample1, sample2))) + viewModel.handleResult(BiometricReferenceCapture("", Modality.FINGERPRINT, format, listOf(capture1, capture2))) viewModel.currentStep.test().value().peekContent()?.let { step -> assertThat(step.id).isEqualTo(StepId.FINGERPRINT_MATCHER) val params = step.params?.let { it as? MatchParams } assertThat(params).isNotNull() assertThat(params?.bioSdk).isEqualTo(SECUGEN_SIM_MATCHER) - assertThat(params?.probeSamples?.size).isEqualTo(2) - assertThat(params?.probeSamples?.get(0)?.format).isEqualTo(format) - assertThat(params?.probeSamples?.get(1)?.format).isEqualTo(format) + assertThat(params?.probeReference?.templates?.size).isEqualTo(2) + assertThat(params?.probeReference?.format).isEqualTo(format) } } @@ -422,7 +417,7 @@ internal class OrchestratorViewModelTest { ) viewModel.handleAction(mockk()) - viewModel.handleResult(CaptureIdentity("", Modality.FINGERPRINT, emptyList())) + viewModel.handleResult(BiometricReferenceCapture("", Modality.FINGERPRINT, "format", emptyList())) viewModel.currentStep.test().value().peekContent()?.let { step -> assertThat(step.params?.let { it as? EnrolLastBiometricParams }?.steps).containsExactly(mockEnrolLastStep) @@ -432,30 +427,26 @@ internal class OrchestratorViewModelTest { @Test fun `Updates external credential step payload with fingerprint samples when receiving fingerprint capture result`() = runTest { val fingerprintReferenceId = "fingerprintReferenceId" - val fingerId1 = SampleIdentifier.LEFT_INDEX_FINGER - val fingerId2 = SampleIdentifier.RIGHT_THUMB + val fingerId1 = TemplateIdentifier.LEFT_INDEX_FINGER + val fingerId2 = TemplateIdentifier.RIGHT_THUMB val template1 = ByteArray(10) val template2 = ByteArray(20) val format1 = "format1" val format2 = "format2" - val fingerprintSample1 = CaptureSample( + val fingerprintCapture1 = BiometricTemplateCapture( captureEventId = GUID1, - modality = Modality.FINGERPRINT, identifier = fingerId1, template = template1, - format = format1, ) - val fingerprintSample2 = CaptureSample( + val fingerprintCapture2 = BiometricTemplateCapture( captureEventId = GUID2, - modality = Modality.FINGERPRINT, identifier = fingerId2, template = template2, - format = format2, ) val externalCredentialParams = mockk(relaxed = true) { - every { copy(probeReferenceId = any(), samples = any()) } returns this + every { copy(probeReferences = any()) } returns this } coEvery { stepsBuilder.build(any(), any(), any(), any()) } returns listOf( @@ -466,18 +457,25 @@ internal class OrchestratorViewModelTest { viewModel.handleAction(mockk()) viewModel.handleResult( - CaptureIdentity(fingerprintReferenceId, Modality.FINGERPRINT, listOf(fingerprintSample1, fingerprintSample2)), + BiometricReferenceCapture( + fingerprintReferenceId, + Modality.FINGERPRINT, + format1, + listOf(fingerprintCapture1, fingerprintCapture2), + ), ) - val expectedFingerprintSamples = listOf( - CaptureSample(GUID1, Modality.FINGERPRINT, format1, template1, fingerId1), - CaptureSample(GUID2, Modality.FINGERPRINT, format2, template2, fingerId2), - ) + val expectedFingerprintReference = + BiometricReferenceCapture( + fingerprintReferenceId, + Modality.FINGERPRINT, + format1, + listOf(fingerprintCapture1, fingerprintCapture2), + ) verify { externalCredentialParams.copy( - probeReferenceId = fingerprintReferenceId, - samples = mapOf(Modality.FINGERPRINT to expectedFingerprintSamples), + probeReferences = listOf(expectedFingerprintReference), ) } } 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 93436264d1..5ec38db473 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 @@ -3,15 +3,15 @@ package com.simprints.feature.orchestrator.cache import android.content.SharedPreferences import androidx.test.ext.junit.runners.* import com.google.common.truth.Truth.* +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality +import com.simprints.core.domain.common.TemplateIdentifier +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.domain.externalcredential.ExternalCredentialType import com.simprints.core.domain.response.AppErrorReason -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.MatchComparisonResult -import com.simprints.core.domain.sample.SampleIdentifier import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw @@ -49,7 +49,7 @@ import com.simprints.fingerprint.capture.FingerprintCaptureParams import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 import com.simprints.infra.matching.MatchParams import com.simprints.infra.matching.MatchResult @@ -126,23 +126,25 @@ class OrchestratorCacheIntegrationTest { moduleId = TokenizableString.Raw("value"), steps = listOf( EnrolLastBiometricStepResult.CaptureResult( - "referenceId", - listOf( - CaptureSample( - captureEventId = GUID1, - identifier = SampleIdentifier.LEFT_THUMB, - template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", + BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = GUID1, + identifier = TemplateIdentifier.LEFT_THUMB, + template = byteArrayOf(1, 2, 3), + ), ), ), ), EnrolLastBiometricStepResult.MatchResult( - listOf(MatchComparisonResult("subjectId", 0.5f)), + listOf(ComparisonResult("subjectId", 0.5f)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), EnrolLastBiometricStepResult.MatchResult( - listOf(MatchComparisonResult("subjectId", 0.5f)), + listOf(ComparisonResult("subjectId", 0.5f)), FaceConfiguration.BioSdk.RANK_ONE, ), EnrolLastBiometricStepResult.EnrolLastBiometricsResult("subjectId"), @@ -164,7 +166,7 @@ class OrchestratorCacheIntegrationTest { id = StepId.VALIDATE_ID_POOL, navigationActionId = 5, destinationId = 6, - params = ValidateSubjectPoolFragmentParams(SubjectQuery()), + params = ValidateSubjectPoolFragmentParams(EnrolmentRecordQuery()), status = StepStatus.COMPLETED, result = ValidateSubjectPoolResult(true), ), @@ -184,24 +186,28 @@ class OrchestratorCacheIntegrationTest { subjectId = "subjectId", flowType = FlowType.IDENTIFY, ageGroup = AgeGroup(1, 2), - probeReferenceId = "referenceId", - samples = mapOf( - Modality.FACE to listOf( - CaptureSample( - captureEventId = GUID1, - identifier = SampleIdentifier.LEFT_THUMB, - template = byteArrayOf(1, 2, 3), - modality = Modality.FACE, - format = "format", + probeReferences = listOf( + BiometricReferenceCapture( + referenceId = "referenceId1", + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureEvent1", + identifier = TemplateIdentifier.LEFT_THUMB, + template = byteArrayOf(1, 2, 3), + ), ), ), - Modality.FINGERPRINT to listOf( - CaptureSample( - captureEventId = GUID1, - identifier = SampleIdentifier.LEFT_THUMB, - template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", + BiometricReferenceCapture( + referenceId = "referenceId1", + modality = Modality.FACE, + format = "format2", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureEvent2", + template = byteArrayOf(2, 3, 4), + ), ), ), ), @@ -223,7 +229,7 @@ class OrchestratorCacheIntegrationTest { matchResults = listOf( CredentialMatch( credential = "credential".asTokenizableEncrypted(), - matchResult = MatchComparisonResult("subjectId", 0.5f), + comparisonResult = ComparisonResult("subjectId", 0.5f), verificationThreshold = 55f, bioSdk = FaceConfiguration.BioSdk.RANK_ONE, ), @@ -250,20 +256,19 @@ class OrchestratorCacheIntegrationTest { destinationId = 4, params = FingerprintCaptureParams( flowType = FlowType.ENROL, - fingerprintsToCapture = listOf(SampleIdentifier.LEFT_4TH_FINGER), + fingerprintsToCapture = listOf(TemplateIdentifier.LEFT_4TH_FINGER), fingerprintSDK = FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), status = StepStatus.COMPLETED, - result = CaptureIdentity( + result = BiometricReferenceCapture( "", modality = Modality.FINGERPRINT, - samples = listOf( - CaptureSample( + format = "format", + templates = listOf( + BiometricTemplateCapture( captureEventId = GUID1, - identifier = SampleIdentifier.LEFT_THUMB, + identifier = TemplateIdentifier.LEFT_THUMB, template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", ), ), ), @@ -273,24 +278,26 @@ class OrchestratorCacheIntegrationTest { navigationActionId = 3, destinationId = 4, params = MatchParams( - probeReferenceId = GUID1, flowType = FlowType.IDENTIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.CommCare("name"), bioSdk = FingerprintConfiguration.BioSdk.NEC, - probeSamples = listOf( - CaptureSample( - captureEventId = GUID1, - identifier = SampleIdentifier.LEFT_THUMB, - template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", + probeReference = BiometricReferenceCapture( + referenceId = GUID1, + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureEvent1", + identifier = TemplateIdentifier.LEFT_THUMB, + template = byteArrayOf(1, 2, 3), + ), ), ), ), status = StepStatus.COMPLETED, result = MatchResult( - listOf(MatchComparisonResult("subjectId", 0.5f)), + listOf(ComparisonResult("subjectId", 0.5f)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), ), @@ -314,16 +321,14 @@ class OrchestratorCacheIntegrationTest { destinationId = 6, params = FaceCaptureParams(3, FaceConfiguration.BioSdk.RANK_ONE), status = StepStatus.COMPLETED, - result = CaptureIdentity( + result = BiometricReferenceCapture( "", modality = Modality.FACE, - samples = listOf( - CaptureSample( + format = "ROC", + templates = listOf( + BiometricTemplateCapture( captureEventId = GUID1, - identifier = SampleIdentifier.LEFT_THUMB, template = byteArrayOf(1, 2, 3), - modality = Modality.FACE, - format = "ROC", ), ), ), @@ -333,24 +338,25 @@ class OrchestratorCacheIntegrationTest { navigationActionId = 3, destinationId = 4, params = MatchParams( - probeReferenceId = GUID1, flowType = FlowType.IDENTIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, bioSdk = FaceConfiguration.BioSdk.RANK_ONE, - probeSamples = listOf( - CaptureSample( - captureEventId = GUID1, - identifier = SampleIdentifier.LEFT_THUMB, - template = byteArrayOf(1, 2, 3), - modality = Modality.FACE, - format = "format", + probeReference = BiometricReferenceCapture( + referenceId = GUID1, + modality = Modality.FACE, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureEvent1", + template = byteArrayOf(1, 2, 3), + ), ), ), ), status = StepStatus.COMPLETED, result = MatchResult( - listOf(MatchComparisonResult("subjectId", 0.5f)), + listOf(ComparisonResult("subjectId", 0.5f)), FaceConfiguration.BioSdk.RANK_ONE, ), ), diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt index 18606bf17b..52ba10a6ba 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt @@ -3,7 +3,7 @@ package com.simprints.feature.orchestrator.usecases import com.google.common.truth.Truth.* import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureIdentity +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.feature.alert.AlertResult import com.simprints.feature.exitform.ExitFormOption import com.simprints.feature.exitform.ExitFormResult @@ -62,7 +62,7 @@ class MapRefusalOrErrorResultUseCaseTest { listOf( FetchSubjectResult(found = true), SetupResult(isSuccess = true), - CaptureIdentity("", modality = Modality.FACE, emptyList()), + BiometricReferenceCapture("", modality = Modality.FACE, "format", emptyList()), ).forEach { result -> assertThat(useCase(result, mockk())).isNull() } } 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 0e0a2fb096..5688c0e2a6 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 @@ -2,14 +2,13 @@ package com.simprints.feature.orchestrator.usecases import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.feature.enrollast.EnrolLastBiometricResult import com.simprints.feature.enrollast.EnrolLastBiometricStepResult import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration -import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 import com.simprints.infra.matching.MatchResult import org.junit.Before import org.junit.Test @@ -45,17 +44,16 @@ internal class MapStepsForLastBiometricEnrolUseCaseTest { } @Test - fun `maps face CaptureIdentity correctly`() { + fun `maps face BiometricReferenceCapture correctly`() { val result = useCase( listOf( - CaptureIdentity( + BiometricReferenceCapture( "referenceId", modality = Modality.FACE, - samples = listOf( - CaptureSample( - captureEventId = GUID1, - modality = Modality.FACE, - format = "format", + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureId", template = byteArrayOf(), ), ), @@ -65,13 +63,15 @@ internal class MapStepsForLastBiometricEnrolUseCaseTest { assertThat(result.first()).isEqualTo( EnrolLastBiometricStepResult.CaptureResult( - referenceId = "referenceId", - results = listOf( - CaptureSample( - captureEventId = "captureId", - template = byteArrayOf(), - format = "format", - modality = Modality.FACE, + result = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureId", + template = byteArrayOf(), + ), ), ), ), @@ -95,16 +95,15 @@ internal class MapStepsForLastBiometricEnrolUseCaseTest { fun `maps fingerprint CaptureIdentity correctly`() { val result = useCase( listOf( - CaptureIdentity( + BiometricReferenceCapture( "referenceId", modality = Modality.FINGERPRINT, - samples = listOf( - CaptureSample( - captureEventId = GUID1, - modality = Modality.FINGERPRINT, - format = "format", + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureId", template = byteArrayOf(), - identifier = SampleIdentifier.RIGHT_THUMB, + identifier = TemplateIdentifier.RIGHT_THUMB, ), ), ), @@ -113,14 +112,16 @@ internal class MapStepsForLastBiometricEnrolUseCaseTest { assertThat(result.first()).isEqualTo( EnrolLastBiometricStepResult.CaptureResult( - referenceId = "referenceId", - results = listOf( - CaptureSample( - captureEventId = "captureId", - template = byteArrayOf(), - format = "format", - identifier = SampleIdentifier.RIGHT_THUMB, - modality = Modality.FINGERPRINT, + result = BiometricReferenceCapture( + "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "captureId", + template = byteArrayOf(), + identifier = TemplateIdentifier.RIGHT_THUMB, + ), ), ), ), diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCaseTest.kt index 320fec47de..27f2d72b92 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateEnrolResponseUseCaseTest.kt @@ -3,14 +3,14 @@ package com.simprints.feature.orchestrator.usecases.response import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.CaptureIdentity +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential import com.simprints.feature.orchestrator.exceptions.MissingCaptureException import com.simprints.infra.config.store.models.Project -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import com.simprints.infra.orchestration.data.ActionRequest import com.simprints.infra.orchestration.data.responses.AppEnrolResponse import com.simprints.infra.orchestration.data.responses.AppErrorResponse @@ -22,10 +22,10 @@ import org.junit.Test internal class CreateEnrolResponseUseCaseTest { @MockK - lateinit var subjectFactory: SubjectFactory + lateinit var enrolmentRecordFactory: EnrolmentRecordFactory @MockK - lateinit var enrolSubject: EnrolSubjectUseCase + lateinit var enrolRecord: EnrolRecordUseCase @MockK lateinit var project: Project @@ -46,15 +46,15 @@ internal class CreateEnrolResponseUseCaseTest { fun setUp() { MockKAnnotations.init(this, relaxed = true) - coJustRun { enrolSubject.invoke(any(), any()) } + coJustRun { enrolRecord.invoke(any(), any()) } - useCase = CreateEnrolResponseUseCase(subjectFactory, enrolSubject) + useCase = CreateEnrolResponseUseCase(enrolmentRecordFactory, enrolRecord) } @Test fun `Converts correct results to response`() = runTest { every { - subjectFactory.buildSubjectFromCaptureResults( + enrolmentRecordFactory.buildFromCaptureResults( subjectId = any(), projectId = any(), attendantId = any(), @@ -68,8 +68,8 @@ internal class CreateEnrolResponseUseCaseTest { useCase( request = action, results = listOf( - CaptureIdentity("", Modality.FINGERPRINT, emptyList()), - CaptureIdentity("", Modality.FACE, emptyList()), + BiometricReferenceCapture("", Modality.FINGERPRINT, "", emptyList()), + BiometricReferenceCapture("", Modality.FACE, "", emptyList()), mockk(), ), project = project, @@ -81,7 +81,7 @@ internal class CreateEnrolResponseUseCaseTest { @Test fun `Returns error if no valid response`() = runTest { every { - subjectFactory.buildSubjectFromCaptureResults( + enrolmentRecordFactory.buildFromCaptureResults( subjectId = any(), projectId = any(), attendantId = any(), @@ -107,7 +107,7 @@ internal class CreateEnrolResponseUseCaseTest { } every { - subjectFactory.buildSubjectFromCaptureResults( + enrolmentRecordFactory.buildFromCaptureResults( subjectId = any(), projectId = any(), attendantId = any(), @@ -120,7 +120,7 @@ internal class CreateEnrolResponseUseCaseTest { useCase( request = action, results = listOf( - CaptureIdentity("", Modality.FINGERPRINT, emptyList()), + BiometricReferenceCapture("", Modality.FINGERPRINT, "", emptyList()), credentialSearchResult, ), project = project, @@ -128,7 +128,7 @@ internal class CreateEnrolResponseUseCaseTest { ) verify { - subjectFactory.buildSubjectFromCaptureResults( + enrolmentRecordFactory.buildFromCaptureResults( subjectId = enrolmentSubjectId, projectId = projectId, attendantId = any(), diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCaseTest.kt index 649c14d63a..37bdee464a 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateIdentifyResponseUseCaseTest.kt @@ -1,7 +1,7 @@ package com.simprints.feature.orchestrator.usecases.response import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.model.CredentialMatch import com.simprints.infra.config.store.models.DecisionPolicy @@ -207,16 +207,16 @@ class CreateIdentifyResponseUseCaseTest { val faceMatches = listOf( mockk { every { verificationThreshold } returns 0.0f - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = faceSmallConfidence, - confidence = smallConfidence, + comparisonScore = smallConfidence, ) every { bioSdk } returns FaceConfiguration.BioSdk.RANK_ONE }, mockk { - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = faceBigConfidence, - confidence = bigConfidence, + comparisonScore = bigConfidence, ) every { bioSdk } returns FaceConfiguration.BioSdk.RANK_ONE }, @@ -224,9 +224,9 @@ class CreateIdentifyResponseUseCaseTest { val fingerprintMatches = listOf( mockk { - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = "fingerprintSubjectId", - confidence = 90f, + comparisonScore = 90f, ) every { bioSdk } returns FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER }, @@ -260,16 +260,16 @@ class CreateIdentifyResponseUseCaseTest { val fingerprintMatches = listOf( mockk { every { verificationThreshold } returns 0.0f - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = fingerprintSmallConfidence, - confidence = smallConfidence, + comparisonScore = smallConfidence, ) every { bioSdk } returns FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER }, mockk { - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = fingerprintBigConfidence, - confidence = bigConfidence, + comparisonScore = bigConfidence, ) every { bioSdk } returns FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER }, @@ -277,9 +277,9 @@ class CreateIdentifyResponseUseCaseTest { val faceMatches = listOf( mockk { - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = "faceSubjectId", - confidence = 90f, + comparisonScore = 90f, ) every { bioSdk } returns FaceConfiguration.BioSdk.RANK_ONE }, @@ -314,9 +314,9 @@ class CreateIdentifyResponseUseCaseTest { val credentialFaceMatches = listOf( mockk { - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = sharedGuid, - confidence = credentialConfidence, + comparisonScore = credentialConfidence, ) every { bioSdk } returns FaceConfiguration.BioSdk.RANK_ONE }, @@ -335,7 +335,7 @@ class CreateIdentifyResponseUseCaseTest { every { matchResults } returns credentialFaceMatches }, MatchResult( - listOf(MatchComparisonResult(subjectId = sharedGuid, confidence = faceConfidence)), + listOf(ComparisonResult(subjectId = sharedGuid, comparisonScore = faceConfidence)), FaceConfiguration.BioSdk.RANK_ONE, ), ), @@ -355,9 +355,9 @@ class CreateIdentifyResponseUseCaseTest { val credentialFingerprintMatches = listOf( mockk { every { verificationThreshold } returns 0.0f - every { matchResult } returns MatchComparisonResult( + every { comparisonResult } returns ComparisonResult( subjectId = sharedGuid, - confidence = credentialConfidence, + comparisonScore = credentialConfidence, ) every { bioSdk } returns FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER }, @@ -376,7 +376,7 @@ class CreateIdentifyResponseUseCaseTest { every { matchResults } returns credentialFingerprintMatches }, MatchResult( - listOf(MatchComparisonResult(subjectId = sharedGuid, confidence = fingerprintConfidence)), + listOf(ComparisonResult(subjectId = sharedGuid, comparisonScore = fingerprintConfidence)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), ), @@ -388,12 +388,12 @@ class CreateIdentifyResponseUseCaseTest { } private fun createFaceMatchResult(vararg confidences: Float): Serializable = MatchResult( - confidences.mapIndexed { i, confidence -> MatchComparisonResult(subjectId = "$i", confidence = confidence) }, + confidences.mapIndexed { i, confidence -> ComparisonResult(subjectId = "$i", comparisonScore = confidence) }, FaceConfiguration.BioSdk.RANK_ONE, ) private fun createFingerprintMatchResult(vararg confidences: Float): Serializable = MatchResult( - confidences.mapIndexed { i, confidence -> MatchComparisonResult(subjectId = "$i", confidence = confidence) }, + confidences.mapIndexed { i, confidence -> ComparisonResult(subjectId = "$i", comparisonScore = confidence) }, FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ) } diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCaseTest.kt index 15d982fd83..0461d445aa 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/CreateVerifyResponseUseCaseTest.kt @@ -1,7 +1,7 @@ package com.simprints.feature.orchestrator.usecases.response import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.infra.config.store.models.DecisionPolicy import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration @@ -248,12 +248,12 @@ class CreateVerifyResponseUseCaseTest { } private fun createFingerprintMatchResult(vararg confidences: Float): Serializable = MatchResult( - confidences.map { MatchComparisonResult(subjectId = "1", confidence = it) }, + confidences.map { ComparisonResult(subjectId = "1", comparisonScore = it) }, FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ) private fun createFaceMatchResult(vararg confidences: Float): Serializable = MatchResult( - confidences.map { MatchComparisonResult(subjectId = "1", confidence = it) }, + confidences.map { ComparisonResult(subjectId = "1", comparisonScore = it) }, FaceConfiguration.BioSdk.RANK_ONE, ) } diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/EnrolSubjectUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/EnrolRecordUseCaseTest.kt similarity index 93% rename from feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/EnrolSubjectUseCaseTest.kt rename to feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/EnrolRecordUseCaseTest.kt index 29648d3742..b96bd43444 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/EnrolSubjectUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/EnrolRecordUseCaseTest.kt @@ -6,8 +6,8 @@ import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.events.event.domain.models.BiometricReferenceCreationEvent import com.simprints.infra.events.event.domain.models.BiometricReferenceCreationEvent.BiometricReferenceCreationPayload import com.simprints.infra.events.event.domain.models.EnrolmentEventV4 @@ -23,7 +23,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -class EnrolSubjectUseCaseTest { +class EnrolRecordUseCaseTest { @MockK private lateinit var eventRepository: SessionEventRepository @@ -36,7 +36,7 @@ class EnrolSubjectUseCaseTest { @MockK private lateinit var project: Project - private lateinit var useCase: EnrolSubjectUseCase + private lateinit var useCase: EnrolRecordUseCase @Before fun setUp() { @@ -46,7 +46,7 @@ class EnrolSubjectUseCaseTest { every { id } returns "sessionId" } - useCase = EnrolSubjectUseCase( + useCase = EnrolRecordUseCase( eventRepository, timeHelper, enrolmentRecordRepository, @@ -62,7 +62,7 @@ class EnrolSubjectUseCaseTest { ) useCase.invoke( - Subject( + EnrolmentRecord( subjectId = "subjectId", projectId = "projectId", attendantId = "moduleId".asTokenizableRaw(), @@ -87,7 +87,7 @@ class EnrolSubjectUseCaseTest { ) useCase.invoke( - Subject( + EnrolmentRecord( subjectId = "subjectId", projectId = "projectId", attendantId = "moduleId".asTokenizableRaw(), @@ -99,7 +99,7 @@ class EnrolSubjectUseCaseTest { coVerify { enrolmentRecordRepository.performActions( withArg { - assertThat(it.first()).isInstanceOf(SubjectAction.Creation::class.java) + assertThat(it.first()).isInstanceOf(EnrolmentRecordAction.Creation::class.java) }, project, ) @@ -126,7 +126,7 @@ class EnrolSubjectUseCaseTest { ) useCase.invoke( - Subject( + EnrolmentRecord( subjectId = "subjectId", projectId = "projectId", attendantId = "moduleId".asTokenizableRaw(), diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCaseTest.kt index 50d74b0669..eb5a27b837 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/response/IsNewEnrolmentUseCaseTest.kt @@ -1,7 +1,7 @@ package com.simprints.feature.orchestrator.usecases.response import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.model.CredentialMatch import com.simprints.infra.config.store.models.DecisionPolicy @@ -53,7 +53,7 @@ internal class IsNewEnrolmentUseCaseTest { projectConfiguration, listOf( MatchResult( - listOf(MatchComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), + listOf(ComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), ), @@ -70,7 +70,7 @@ internal class IsNewEnrolmentUseCaseTest { projectConfiguration, listOf( MatchResult( - listOf(MatchComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), + listOf(ComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), ), @@ -85,7 +85,7 @@ internal class IsNewEnrolmentUseCaseTest { assertThat( useCase( projectConfiguration, - listOf(MatchResult(listOf(MatchComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE)), + listOf(MatchResult(listOf(ComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE)), ), ).isTrue() } @@ -97,7 +97,7 @@ internal class IsNewEnrolmentUseCaseTest { assertThat( useCase( projectConfiguration, - listOf(MatchResult(listOf(MatchComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE)), + listOf(MatchResult(listOf(ComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE)), ), ).isFalse() } @@ -111,10 +111,10 @@ internal class IsNewEnrolmentUseCaseTest { projectConfiguration, listOf( MatchResult( - listOf(MatchComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), + listOf(ComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), - MatchResult(listOf(MatchComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE), + MatchResult(listOf(ComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE), ), ), ).isTrue() @@ -129,10 +129,10 @@ internal class IsNewEnrolmentUseCaseTest { projectConfiguration, listOf( MatchResult( - listOf(MatchComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), + listOf(ComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), - MatchResult(listOf(MatchComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE), + MatchResult(listOf(ComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE), ), ), ).isFalse() @@ -147,10 +147,10 @@ internal class IsNewEnrolmentUseCaseTest { projectConfiguration, listOf( MatchResult( - listOf(MatchComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), + listOf(ComparisonResult("", HIGHER_THAN_MEDIUM_SCORE)), FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, ), - MatchResult(listOf(MatchComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE), + MatchResult(listOf(ComparisonResult("", LOWER_THAN_MEDIUM_SCORE)), FaceConfiguration.BioSdk.RANK_ONE), ), ), ).isFalse() @@ -162,9 +162,9 @@ internal class IsNewEnrolmentUseCaseTest { val credentialMatches = listOf( mockk { - every { matchResult } returns mockk { + every { comparisonResult } returns mockk { every { subjectId } returns "subjectId" - every { confidence } returns 50f + every { comparisonScore } returns 50f } every { bioSdk } returns FaceConfiguration.BioSdk.RANK_ONE }, diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCaseTest.kt index 83098ec320..354c73ac45 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCaseTest.kt @@ -4,7 +4,7 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential import com.simprints.feature.orchestrator.cache.OrchestratorCache import com.simprints.feature.orchestrator.exceptions.SubjectAgeNotSupportedException @@ -75,16 +75,16 @@ class BuildStepsUseCaseTest { ) every { secugenSimMatcher.fingersToCapture } returns listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_THUMB, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_THUMB, ) every { secugenSimMatcher.allowedAgeRange } returns AgeGroup(0, null) every { projectConfiguration.fingerprint?.secugenSimMatcher } returns secugenSimMatcher every { projectConfiguration.fingerprint?.getSdkConfiguration(SECUGEN_SIM_MATCHER) } returns secugenSimMatcher every { nec.fingersToCapture } returns listOf( - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, ) every { nec.allowedAgeRange } returns AgeGroup(0, null) every { projectConfiguration.fingerprint?.nec } returns nec diff --git a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModel.kt b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModel.kt index 42026cda90..15e3777365 100644 --- a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModel.kt +++ b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModel.kt @@ -21,7 +21,7 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.event.domain.models.EnrolmentUpdateEvent import com.simprints.infra.events.event.domain.models.ExternalCredentialCaptureValueEvent import com.simprints.infra.events.event.domain.models.GuidSelectionEvent @@ -101,7 +101,7 @@ internal class SelectSubjectViewModel @AssistedInject constructor( val project = configManager.getProject() ?: return null val alreadyLinkedSubject = enrolmentRecordRepository .load( - SubjectQuery( + EnrolmentRecordQuery( projectId = project.id, subjectId = subjectId, externalCredential = credential, diff --git a/feature/select-subject/src/test/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModelTest.kt b/feature/select-subject/src/test/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModelTest.kt index a8039d6b8f..8f48f2b6ea 100644 --- a/feature/select-subject/src/test/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModelTest.kt +++ b/feature/select-subject/src/test/java/com/simprints/feature/selectsubject/screen/SelectSubjectViewModelTest.kt @@ -18,7 +18,7 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.events.event.domain.models.EnrolmentUpdateEvent import com.simprints.infra.events.event.domain.models.ExternalCredentialCaptureEvent import com.simprints.infra.events.event.domain.models.ExternalCredentialSelectionEvent @@ -163,7 +163,7 @@ internal class SelectSubjectViewModelTest { fun `displays credential dialog when credential is scanned and linked to different subject`() = runTest { val scannedCredential = mockk(relaxed = true) val displayedCredential = mockk(relaxed = true) - val repositoryResponse = listOf(mockk { every { subjectId } returns "not_this_subject_id" }) + val repositoryResponse = listOf(mockk { every { subjectId } returns "not_this_subject_id" }) setupCredentialState(displayedCredential, repositoryResponse = repositoryResponse) val viewModel = createViewModel(params = selectSubjectParams.copy(scannedCredential = scannedCredential)) @@ -185,7 +185,7 @@ internal class SelectSubjectViewModelTest { every { credentialType } returns type } val displayedCredential = mockk(relaxed = true) - val repositoryResponse = listOf(mockk { every { subjectId } returns SUBJECT_ID }) + val repositoryResponse = listOf(mockk { every { subjectId } returns SUBJECT_ID }) setupCredentialState(displayedCredential, repositoryResponse = repositoryResponse) val viewModel = createViewModel(params = selectSubjectParams.copy(scannedCredential = scannedCredential)) @@ -209,7 +209,7 @@ internal class SelectSubjectViewModelTest { every { credentialType } returns type } val displayedCredential = mockk(relaxed = true) - val repositoryResponse = listOf(mockk { every { subjectId } returns SUBJECT_ID }) + val repositoryResponse = listOf(mockk { every { subjectId } returns SUBJECT_ID }) setupCredentialState(displayedCredential, repositoryResponse = repositoryResponse) val viewModel = createViewModel( @@ -230,7 +230,7 @@ internal class SelectSubjectViewModelTest { fun `does not display credential dialog when project not availalbe`() = runTest { val scannedCredential = mockk(relaxed = true) val displayedCredential = mockk(relaxed = true) - val repositoryResponse = listOf(mockk { every { subjectId } returns "not_this_subject_id" }) + val repositoryResponse = listOf(mockk { every { subjectId } returns "not_this_subject_id" }) setupCredentialState( displayedCredential, repositoryResponse = repositoryResponse, @@ -266,7 +266,7 @@ internal class SelectSubjectViewModelTest { private fun setupCredentialState( displayedCredential: TokenizableString.Raw, - repositoryResponse: List, + repositoryResponse: List, configuredProject: Project? = project, ) { coEvery { authStore.isProjectIdSignedIn(PROJECT_ID) } returns true diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt index 342b1fdfdf..8de51372ab 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt @@ -1,9 +1,9 @@ package com.simprints.feature.validatepool -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery object ValidateSubjectPoolContract { val DESTINATION = R.id.validateSubjectPoolFragment - fun getParams(subjectQuery: SubjectQuery) = ValidateSubjectPoolFragmentParams(subjectQuery) + fun getParams(enrolmentRecordQuery: EnrolmentRecordQuery) = ValidateSubjectPoolFragmentParams(enrolmentRecordQuery) } diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolFragmentParams.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolFragmentParams.kt index 321c02a780..00c1a98cf8 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolFragmentParams.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolFragmentParams.kt @@ -2,9 +2,9 @@ package com.simprints.feature.validatepool import androidx.annotation.Keep import com.simprints.core.domain.step.StepParams -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery @Keep data class ValidateSubjectPoolFragmentParams( - val subjectQuery: SubjectQuery, + val enrolmentRecordQuery: EnrolmentRecordQuery, ) : StepParams diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt index 058dcd8cd2..04810c2376 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt @@ -39,13 +39,14 @@ internal class ValidateSubjectPoolFragment : Fragment(R.layout.fragment_validate binding.validationActionsClose.setOnClickListener { finishWithResult(false) } binding.validationActionsContinue.setOnClickListener { finishWithResult(true) } - binding.validationActionsSync.setOnClickListener { viewModel.syncAndRetry(params.subjectQuery) } + binding.validationActionsSync.setOnClickListener { viewModel.syncAndRetry(params.enrolmentRecordQuery) } - viewModel.checkIdentificationPool(params.subjectQuery) + viewModel.checkIdentificationPool(params.enrolmentRecordQuery) } private fun renderState(state: ValidateSubjectPoolState) = when (state) { ValidateSubjectPoolState.Success -> finishWithResult(true) + ValidateSubjectPoolState.Validating -> setViews( titleRes = IDR.string.id_pool_validation_default_message, ) diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModel.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModel.kt index 76948c5d4a..91c0b84292 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModel.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModel.kt @@ -10,7 +10,7 @@ import com.simprints.feature.validatepool.usecase.HasRecordsUseCase import com.simprints.feature.validatepool.usecase.IsModuleIdNotSyncedUseCase import com.simprints.feature.validatepool.usecase.RunBlockingEventSyncUseCase import com.simprints.feature.validatepool.usecase.ShouldSuggestSyncUseCase -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -27,16 +27,16 @@ internal class ValidateSubjectPoolViewModel @Inject constructor( private var _state = MutableLiveData>() private var isSyncing: Boolean = false - fun checkIdentificationPool(subjectQuery: SubjectQuery) = viewModelScope.launch { + fun checkIdentificationPool(enrolmentRecordQuery: EnrolmentRecordQuery) = viewModelScope.launch { if (isSyncing) { return@launch } _state.send(ValidateSubjectPoolState.Validating) val validationState = when { - hasRecords(subjectQuery) -> ValidateSubjectPoolState.Success - subjectQuery.attendantId != null && hasRecords(SubjectQuery()) -> ValidateSubjectPoolState.UserMismatch - subjectQuery.moduleId?.let { isModuleIdNotSynced(it) } == true -> ValidateSubjectPoolState.ModuleMismatch + hasRecords(enrolmentRecordQuery) -> ValidateSubjectPoolState.Success + enrolmentRecordQuery.attendantId != null && hasRecords(EnrolmentRecordQuery()) -> ValidateSubjectPoolState.UserMismatch + enrolmentRecordQuery.moduleId?.let { isModuleIdNotSynced(it) } == true -> ValidateSubjectPoolState.ModuleMismatch shouldSuggestSync() -> ValidateSubjectPoolState.RequiresSync else -> ValidateSubjectPoolState.PoolEmpty } @@ -44,11 +44,11 @@ internal class ValidateSubjectPoolViewModel @Inject constructor( _state.send(validationState) } - fun syncAndRetry(subjectQuery: SubjectQuery) = viewModelScope.launch { + fun syncAndRetry(enrolmentRecordQuery: EnrolmentRecordQuery) = viewModelScope.launch { _state.send(ValidateSubjectPoolState.SyncInProgress) isSyncing = true runBlockingSync() isSyncing = false - checkIdentificationPool(subjectQuery) + checkIdentificationPool(enrolmentRecordQuery) } } diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCase.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCase.kt index a5d70febdc..93374c2f1b 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCase.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCase.kt @@ -1,11 +1,11 @@ package com.simprints.feature.validatepool.usecase import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import javax.inject.Inject internal class HasRecordsUseCase @Inject constructor( private val enrolmentRepo: EnrolmentRecordRepository, ) { - suspend operator fun invoke(subjectQuery: SubjectQuery) = enrolmentRepo.count(subjectQuery) > 0 + suspend operator fun invoke(enrolmentRecordQuery: EnrolmentRecordQuery) = enrolmentRepo.count(enrolmentRecordQuery) > 0 } diff --git a/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModelTest.kt b/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModelTest.kt index 97e21c95c6..d96df5409c 100644 --- a/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModelTest.kt +++ b/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolViewModelTest.kt @@ -8,7 +8,7 @@ import com.simprints.feature.validatepool.usecase.HasRecordsUseCase import com.simprints.feature.validatepool.usecase.IsModuleIdNotSyncedUseCase import com.simprints.feature.validatepool.usecase.RunBlockingEventSyncUseCase import com.simprints.feature.validatepool.usecase.ShouldSuggestSyncUseCase -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.testtools.common.coroutines.TestCoroutineRule import io.mockk.MockKAnnotations import io.mockk.coEvery @@ -60,18 +60,18 @@ class ValidateSubjectPoolViewModelTest { fun `when subject pool not empty returns Success `() = runTest { coEvery { hasRecordsUseCase(any()) } returns true - viewModel.checkIdentificationPool(SubjectQuery()) + viewModel.checkIdentificationPool(EnrolmentRecordQuery()) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.Success) } @Test fun `if ID by project when not synced recently returns RequiresSync`() = runTest { - val subjectQuery = SubjectQuery(projectId = "projectId") + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = "projectId") coEvery { hasRecordsUseCase(any()) } returns false coEvery { shouldSuggestSyncUseCase() } returns true - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.RequiresSync) coVerify(exactly = 1) { hasRecordsUseCase(any()) } @@ -80,22 +80,22 @@ class ValidateSubjectPoolViewModelTest { @Test fun `if ID by project when synced recently returns PoolEmpty`() = runTest { - val subjectQuery = SubjectQuery(projectId = "module1") + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = "module1") coEvery { hasRecordsUseCase(any()) } returns false coEvery { shouldSuggestSyncUseCase() } returns false - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.PoolEmpty) } @Test fun `if ID by user when subjects enrolled under other attendant ID returns UserMismatch`() = runTest { - val subjectQuery = SubjectQuery(attendantId = "attendantId".asTokenizableEncrypted()) + val enrolmentRecordQuery = EnrolmentRecordQuery(attendantId = "attendantId".asTokenizableEncrypted()) coEvery { hasRecordsUseCase(any()) } returns true - coEvery { hasRecordsUseCase(subjectQuery) } returns false + coEvery { hasRecordsUseCase(enrolmentRecordQuery) } returns false - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.UserMismatch) coVerify(exactly = 0) { isModuleIdNotSyncedUseCase(any()) } @@ -104,11 +104,11 @@ class ValidateSubjectPoolViewModelTest { @Test fun `if ID by user when no subjects and should sync returns RequiredSync`() = runTest { - val subjectQuery = SubjectQuery(attendantId = "attendantId".asTokenizableEncrypted()) + val enrolmentRecordQuery = EnrolmentRecordQuery(attendantId = "attendantId".asTokenizableEncrypted()) coEvery { hasRecordsUseCase(any()) } returns false coEvery { shouldSuggestSyncUseCase() } returns true - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.RequiresSync) coVerify(exactly = 0) { isModuleIdNotSyncedUseCase(any()) } @@ -116,11 +116,11 @@ class ValidateSubjectPoolViewModelTest { @Test fun `if ID by user when no subjects and synced returns PoolEmpty`() = runTest { - val subjectQuery = SubjectQuery(attendantId = "attendantId".asTokenizableEncrypted()) + val enrolmentRecordQuery = EnrolmentRecordQuery(attendantId = "attendantId".asTokenizableEncrypted()) coEvery { hasRecordsUseCase(any()) } returns false coEvery { shouldSuggestSyncUseCase() } returns false - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.PoolEmpty) coVerify(exactly = 0) { isModuleIdNotSyncedUseCase(any()) } @@ -128,11 +128,11 @@ class ValidateSubjectPoolViewModelTest { @Test fun `if ID by module when module is not synced returns ModuleMismatch`() = runTest { - val subjectQuery = SubjectQuery(moduleId = "module1".asTokenizableEncrypted()) + val enrolmentRecordQuery = EnrolmentRecordQuery(moduleId = "module1".asTokenizableEncrypted()) coEvery { hasRecordsUseCase(any()) } returns false coEvery { isModuleIdNotSyncedUseCase(any()) } returns true - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.ModuleMismatch) coVerify(exactly = 1) { hasRecordsUseCase(any()) } @@ -141,38 +141,38 @@ class ValidateSubjectPoolViewModelTest { @Test fun `if ID by module when module is synced and not synced recently returns RequiresSync`() = runTest { - val subjectQuery = SubjectQuery(moduleId = "module1".asTokenizableEncrypted()) + val enrolmentRecordQuery = EnrolmentRecordQuery(moduleId = "module1".asTokenizableEncrypted()) coEvery { hasRecordsUseCase(any()) } returns false coEvery { isModuleIdNotSyncedUseCase(any()) } returns false coEvery { shouldSuggestSyncUseCase() } returns true - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.RequiresSync) } @Test fun `if ID by module when module is synced and synced recently returns PoolEmpty`() = runTest { - val subjectQuery = SubjectQuery(moduleId = "module1".asTokenizableEncrypted()) + val enrolmentRecordQuery = EnrolmentRecordQuery(moduleId = "module1".asTokenizableEncrypted()) coEvery { hasRecordsUseCase(any()) } returns false coEvery { isModuleIdNotSyncedUseCase(any()) } returns false coEvery { shouldSuggestSyncUseCase() } returns false - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) assertThat(viewModel.state.value?.peekContent()).isEqualTo(ValidateSubjectPoolState.PoolEmpty) } @Test fun `runs sync and check`() = runTest { - val subjectQuery = SubjectQuery(projectId = "projectId") + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = "projectId") coEvery { hasRecordsUseCase(any()) } returnsMany listOf(true) coJustRun { runBlockingSync() } val result = viewModel.state.test() - viewModel.syncAndRetry(subjectQuery) + viewModel.syncAndRetry(enrolmentRecordQuery) assertThat(result.valueHistory().map { it.peekContent() }).containsExactly( ValidateSubjectPoolState.SyncInProgress, @@ -185,14 +185,14 @@ class ValidateSubjectPoolViewModelTest { @OptIn(ExperimentalCoroutinesApi::class) @Test fun `checkIdentificationPool not re-validating another time when sync in progress`() = runTest { - val subjectQuery = SubjectQuery(projectId = "projectId") - coEvery { hasRecordsUseCase(subjectQuery) } returns true + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = "projectId") + coEvery { hasRecordsUseCase(enrolmentRecordQuery) } returns true coEvery { runBlockingSync() } coAnswers { delay(1000) } - viewModel.syncAndRetry(subjectQuery) + viewModel.syncAndRetry(enrolmentRecordQuery) - viewModel.checkIdentificationPool(subjectQuery) + viewModel.checkIdentificationPool(enrolmentRecordQuery) advanceUntilIdle() coVerify(exactly = 1) { hasRecordsUseCase(any()) } diff --git a/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCaseTest.kt b/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCaseTest.kt index 4d7a5bd06a..1d098e411c 100644 --- a/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCaseTest.kt +++ b/feature/validate-subject-pool/src/test/java/com/simprints/feature/validatepool/usecase/HasRecordsUseCaseTest.kt @@ -2,7 +2,7 @@ package com.simprints.feature.validatepool.usecase import com.google.common.truth.Truth.assertThat import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.impl.annotations.MockK @@ -26,12 +26,12 @@ class HasRecordsUseCaseTest { @Test fun `Returns false if there are no records`() = runTest { coEvery { repository.count(any()) }.returns(0) - assertThat(usecase(SubjectQuery())).isFalse() + assertThat(usecase(EnrolmentRecordQuery())).isFalse() } @Test fun `Returns true if there are records`() = runTest { coEvery { repository.count(any()) }.returns(1) - assertThat(usecase(SubjectQuery())).isTrue() + assertThat(usecase(EnrolmentRecordQuery())).isTrue() } } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt index e9ef9f00d7..984d04a86c 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt @@ -1,7 +1,7 @@ package com.simprints.fingerprint.capture import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.store.models.FingerprintConfiguration object FingerprintCaptureContract { @@ -9,7 +9,7 @@ object FingerprintCaptureContract { fun getParams( flowType: FlowType, - fingers: List, + fingers: List, fingerprintSDK: FingerprintConfiguration.BioSdk, ) = FingerprintCaptureParams(flowType, fingers, fingerprintSDK) } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt index 84f9f6e6e4..81e00642f7 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt @@ -2,13 +2,13 @@ package com.simprints.fingerprint.capture import androidx.annotation.Keep import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.step.StepParams import com.simprints.infra.config.store.models.FingerprintConfiguration @Keep data class FingerprintCaptureParams( val flowType: FlowType, - val fingerprintsToCapture: List, + val fingerprintsToCapture: List, val fingerprintSDK: FingerprintConfiguration.BioSdk, ) : StepParams diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/models/CaptureId.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/models/CaptureId.kt index c692dc0eb0..11f07cc55b 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/models/CaptureId.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/models/CaptureId.kt @@ -1,8 +1,8 @@ package com.simprints.fingerprint.capture.models -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier internal data class CaptureId( - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val captureIndex: Int, ) diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerIdentifierResources.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerIdentifierResources.kt index ea989b61a1..69d9eac30c 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerIdentifierResources.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerIdentifierResources.kt @@ -2,39 +2,39 @@ package com.simprints.fingerprint.capture.resources import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.R import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageReports import com.simprints.infra.resources.R as IDR @ExcludedFromGeneratedTestCoverageReports("UI code") @DrawableRes -internal fun SampleIdentifier.fingerDrawable(): Int = when (this) { - SampleIdentifier.RIGHT_5TH_FINGER -> R.drawable.hand_bb_r5_c4 - SampleIdentifier.RIGHT_4TH_FINGER -> R.drawable.hand_bb_r4_c4 - SampleIdentifier.RIGHT_3RD_FINGER -> R.drawable.hand_bb_r3_c4 - SampleIdentifier.RIGHT_INDEX_FINGER -> R.drawable.hand_bb_r2_c4 - SampleIdentifier.RIGHT_THUMB -> R.drawable.hand_bb_r1_c4 - SampleIdentifier.LEFT_THUMB -> R.drawable.hand_bb_l1_c4 - SampleIdentifier.LEFT_INDEX_FINGER -> R.drawable.hand_bb_l2_c4 - SampleIdentifier.LEFT_3RD_FINGER -> R.drawable.hand_bb_l3_c4 - SampleIdentifier.LEFT_4TH_FINGER -> R.drawable.hand_bb_l4_c4 - SampleIdentifier.LEFT_5TH_FINGER -> R.drawable.hand_bb_l5_c4 - SampleIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") +internal fun TemplateIdentifier.fingerDrawable(): Int = when (this) { + TemplateIdentifier.RIGHT_5TH_FINGER -> R.drawable.hand_bb_r5_c4 + TemplateIdentifier.RIGHT_4TH_FINGER -> R.drawable.hand_bb_r4_c4 + TemplateIdentifier.RIGHT_3RD_FINGER -> R.drawable.hand_bb_r3_c4 + TemplateIdentifier.RIGHT_INDEX_FINGER -> R.drawable.hand_bb_r2_c4 + TemplateIdentifier.RIGHT_THUMB -> R.drawable.hand_bb_r1_c4 + TemplateIdentifier.LEFT_THUMB -> R.drawable.hand_bb_l1_c4 + TemplateIdentifier.LEFT_INDEX_FINGER -> R.drawable.hand_bb_l2_c4 + TemplateIdentifier.LEFT_3RD_FINGER -> R.drawable.hand_bb_l3_c4 + TemplateIdentifier.LEFT_4TH_FINGER -> R.drawable.hand_bb_l4_c4 + TemplateIdentifier.LEFT_5TH_FINGER -> R.drawable.hand_bb_l5_c4 + TemplateIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") } @ExcludedFromGeneratedTestCoverageReports("UI code") @StringRes -internal fun SampleIdentifier.nameTextId(): Int = when (this) { - SampleIdentifier.RIGHT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_r_5 - SampleIdentifier.RIGHT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_r_4 - SampleIdentifier.RIGHT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_r_3 - SampleIdentifier.RIGHT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_r_2 - SampleIdentifier.RIGHT_THUMB -> IDR.string.fingerprint_capture_finger_r_1 - SampleIdentifier.LEFT_THUMB -> IDR.string.fingerprint_capture_finger_l_1 - SampleIdentifier.LEFT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_l_2 - SampleIdentifier.LEFT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_l_3 - SampleIdentifier.LEFT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_l_4 - SampleIdentifier.LEFT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_l_5 - SampleIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") +internal fun TemplateIdentifier.nameTextId(): Int = when (this) { + TemplateIdentifier.RIGHT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_r_5 + TemplateIdentifier.RIGHT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_r_4 + TemplateIdentifier.RIGHT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_r_3 + TemplateIdentifier.RIGHT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_r_2 + TemplateIdentifier.RIGHT_THUMB -> IDR.string.fingerprint_capture_finger_r_1 + TemplateIdentifier.LEFT_THUMB -> IDR.string.fingerprint_capture_finger_l_1 + TemplateIdentifier.LEFT_INDEX_FINGER -> IDR.string.fingerprint_capture_finger_l_2 + TemplateIdentifier.LEFT_3RD_FINGER -> IDR.string.fingerprint_capture_finger_l_3 + TemplateIdentifier.LEFT_4TH_FINGER -> IDR.string.fingerprint_capture_finger_l_4 + TemplateIdentifier.LEFT_5TH_FINGER -> IDR.string.fingerprint_capture_finger_l_5 + TemplateIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") } 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 6dfd9df73e..d553b21c6e 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 @@ -7,9 +7,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.simprints.core.ExternalScope import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.livedata.LiveDataEvent import com.simprints.core.livedata.LiveDataEventWithContent import com.simprints.core.livedata.send @@ -135,12 +135,12 @@ internal class FingerprintCaptureViewModel @Inject constructor( get() = _invalidLicense private val _invalidLicense = MutableLiveData() - val finishWithFingerprints: LiveData> + val finishWithFingerprints: LiveData> get() = _finishWithFingerprints private val _finishWithFingerprints = - MutableLiveData>() + MutableLiveData>() - private lateinit var originalFingerprintsToCapture: List + private lateinit var originalFingerprintsToCapture: List private val captureEventIds: MutableMap = mutableMapOf() private var lastCaptureStartedAt: Timestamp = Timestamp(0L) private var hasStarted: Boolean = false @@ -166,7 +166,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( } private fun start( - fingerprintsToCapture: List, + fingerprintsToCapture: List, fingerprintSdk: FingerprintConfiguration.BioSdk, ) { if (!hasStarted) { @@ -218,7 +218,9 @@ internal class FingerprintCaptureViewModel @Inject constructor( when (it.currentCaptureState()) { is CaptureState.ScanProcess.Scanning, is CaptureState.ScanProcess.TransferringImage, - -> pauseLiveFeedback() + -> { + pauseLiveFeedback() + } CaptureState.NotCollected, CaptureState.Skipped, @@ -273,7 +275,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( } } - private fun setStartingState(fingerprintsToCapture: List) { + private fun setStartingState(fingerprintsToCapture: List) { val initialState = CollectFingerprintsState.EMPTY.copy( fingerStates = getStartState(fingerprintsToCapture), ) @@ -329,7 +331,10 @@ internal class FingerprintCaptureViewModel @Inject constructor( private fun toggleScanning() { when (state.currentCaptureState()) { - is CaptureState.ScanProcess.Scanning -> cancelScanning() + is CaptureState.ScanProcess.Scanning -> { + cancelScanning() + } + is CaptureState.ScanProcess.TransferringImage -> { // do nothing } @@ -337,7 +342,9 @@ internal class FingerprintCaptureViewModel @Inject constructor( is CaptureState.Skipped, is CaptureState.ScanProcess.NotDetected, is CaptureState.ScanProcess.Collected, - -> startScanning() + -> { + startScanning() + } } } @@ -512,7 +519,10 @@ internal class FingerprintCaptureViewModel @Inject constructor( private fun handleAutoAddFinger() = updateState { state -> when (val nextPriorityFingerId = getNextFingerToAdd(state.fingerStates.map { it.id })) { - null -> state + null -> { + state + } + else -> { val newFingerState = FingerState( id = nextPriorityFingerId, @@ -660,19 +670,18 @@ internal class FingerprintCaptureViewModel @Inject constructor( Simber.i("Finishing fingerprint capture", tag = FINGER_CAPTURE) val resultItems = collectedFingers.mapNotNull { (captureId, collectedFinger) -> captureEventIds[captureId]?.let { captureEventId -> - CaptureSample( + BiometricTemplateCapture( captureEventId = captureEventId, - identifier = captureId.finger, - modality = Modality.FINGERPRINT, - format = collectedFinger.scanResult.templateFormat, template = collectedFinger.scanResult.template, + identifier = captureId.finger, ) } } + val format = bioSdkWrapper.supportedTemplateFormat val biometricReferenceId = UUID.randomUUID().toString() addBiometricReferenceCreationEvents(biometricReferenceId, resultItems.map { it.captureEventId }) - _finishWithFingerprints.send(CaptureIdentity(biometricReferenceId, Modality.FINGERPRINT, resultItems)) + _finishWithFingerprints.send(BiometricReferenceCapture(biometricReferenceId, Modality.FINGERPRINT, format, resultItems)) } private suspend fun saveImageIfExists( @@ -695,7 +704,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( } fun handleOnViewCreated( - fingerprintsToCapture: List, + fingerprintsToCapture: List, fingerprintSdk: FingerprintConfiguration.BioSdk, ) { updateState { diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/FingerState.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/FingerState.kt index bbaa99da4f..9057dd3640 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/FingerState.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/FingerState.kt @@ -1,9 +1,9 @@ package com.simprints.fingerprint.capture.state -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier internal data class FingerState( - val id: SampleIdentifier, + val id: TemplateIdentifier, val captures: List, val currentCaptureIndex: Int = 0, ) { diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCase.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCase.kt index a06d25152c..8da08863ed 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCase.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCase.kt @@ -1,24 +1,24 @@ package com.simprints.fingerprint.capture.usecase -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import javax.inject.Inject internal class GetNextFingerToAddUseCase @Inject constructor() { - operator fun invoke(existingFingers: List): SampleIdentifier? = + operator fun invoke(existingFingers: List): TemplateIdentifier? = DEFAULT_PRIORITY.minus(existingFingers.toSet()).firstOrNull() companion object { private val DEFAULT_PRIORITY = listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_INDEX_FINGER, - SampleIdentifier.LEFT_3RD_FINGER, - SampleIdentifier.RIGHT_3RD_FINGER, - SampleIdentifier.LEFT_4TH_FINGER, - SampleIdentifier.RIGHT_4TH_FINGER, - SampleIdentifier.LEFT_5TH_FINGER, - SampleIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.RIGHT_3RD_FINGER, + TemplateIdentifier.LEFT_4TH_FINGER, + TemplateIdentifier.RIGHT_4TH_FINGER, + TemplateIdentifier.LEFT_5TH_FINGER, + TemplateIdentifier.RIGHT_5TH_FINGER, ) } } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCase.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCase.kt index f445419720..1237ae1f20 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCase.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCase.kt @@ -1,12 +1,12 @@ package com.simprints.fingerprint.capture.usecase -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.state.CaptureState import com.simprints.fingerprint.capture.state.FingerState import javax.inject.Inject internal class GetStartStateUseCase @Inject constructor() { - operator fun invoke(fingerprintsToCapture: List) = fingerprintsToCapture + operator fun invoke(fingerprintsToCapture: List) = fingerprintsToCapture .groupingBy { it } .eachCount() .map { (id, quantity) -> FingerState(id, List(quantity) { CaptureState.NotCollected }) } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCase.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCase.kt index 792d2ca4b4..77e5c86434 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCase.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCase.kt @@ -1,7 +1,7 @@ package com.simprints.fingerprint.capture.usecase import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.extensions.deduceFileExtension import com.simprints.fingerprint.capture.extensions.toInt import com.simprints.fingerprint.capture.state.CaptureState @@ -21,7 +21,7 @@ internal class SaveFingerprintSampleUseCase @Inject constructor( ) { suspend operator fun invoke( vero2Configuration: Vero2Configuration, - finger: SampleIdentifier, + finger: TemplateIdentifier, captureEventId: String?, collectedFinger: CaptureState.ScanProcess.Collected, ) = if (collectedFinger.scanResult.image != null && captureEventId != null) { @@ -45,7 +45,7 @@ internal class SaveFingerprintSampleUseCase @Inject constructor( imageBytes: ByteArray, captureEventId: String, fileExtension: String, - finger: SampleIdentifier, + finger: TemplateIdentifier, dpi: Int, scannerId: String?, un20SerialNumber: String?, diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/confirmfingerprints/ConfirmFingerprintsDialog.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/confirmfingerprints/ConfirmFingerprintsDialog.kt index 6921c8b5fc..18cac56a03 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/confirmfingerprints/ConfirmFingerprintsDialog.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/confirmfingerprints/ConfirmFingerprintsDialog.kt @@ -5,7 +5,7 @@ import android.content.Context import androidx.appcompat.app.AlertDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.resources.nameTextId import com.simprints.infra.resources.R as IDR @@ -38,7 +38,7 @@ internal class ConfirmFingerprintsDialog( }.toString() data class Item( - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val numberOfSuccessfulScans: Int, val numberOfScans: Int, ) diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt index bfe89e809b..254b97fb49 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt @@ -8,7 +8,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.R import com.simprints.fingerprint.capture.databinding.FragmentFingerBinding import com.simprints.fingerprint.capture.resources.directionTextColour @@ -30,7 +30,7 @@ internal class FingerFragment : Fragment(R.layout.fragment_finger) { private val binding by viewBinding(FragmentFingerBinding::bind) private val vm: FingerprintCaptureViewModel by viewModels(ownerProducer = { requireParentFragment() }) - private lateinit var fingerId: SampleIdentifier + private lateinit var fingerId: TemplateIdentifier private lateinit var timeoutBars: List @@ -40,7 +40,7 @@ internal class FingerFragment : Fragment(R.layout.fragment_finger) { ) { super.onViewCreated(view, savedInstanceState) - fingerId = SampleIdentifier.entries.toTypedArray()[ + fingerId = TemplateIdentifier.entries.toTypedArray()[ arguments?.getInt(FINGER_ID_BUNDLE_KEY) ?: throw IllegalArgumentException(), ] @@ -127,7 +127,10 @@ internal class FingerFragment : Fragment(R.layout.fragment_finger) { handleCancelled() } - is CaptureState.ScanProcess.Scanning -> startTimeoutBar() + is CaptureState.ScanProcess.Scanning -> { + startTimeoutBar() + } + is CaptureState.ScanProcess.TransferringImage -> { // Do nothing } @@ -136,10 +139,12 @@ internal class FingerFragment : Fragment(R.layout.fragment_finger) { handleCancelled() } - is CaptureState.ScanProcess.Collected -> if (fingerState.scanResult.isGoodScan()) { - handleCancelled() - } else { - handleCancelled() + is CaptureState.ScanProcess.Collected -> { + if (fingerState.scanResult.isGoodScan()) { + handleCancelled() + } else { + handleCancelled() + } } } } @@ -151,7 +156,7 @@ internal class FingerFragment : Fragment(R.layout.fragment_finger) { private const val PROGRESS_BAR_MARGIN = 4 - fun newInstance(fingerId: SampleIdentifier) = FingerFragment().also { + fun newInstance(fingerId: TemplateIdentifier) = FingerFragment().also { it.arguments = Bundle().apply { putInt(FINGER_ID_BUNDLE_KEY, fingerId.ordinal) } } } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerPageAdapter.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerPageAdapter.kt index 0c534e9b12..d443696dc3 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerPageAdapter.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerPageAdapter.kt @@ -2,13 +2,13 @@ package com.simprints.fingerprint.capture.views.fingerviewpager import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.FragmentStateAdapter -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageReports @ExcludedFromGeneratedTestCoverageReports("UI code") internal class FingerPageAdapter( parent: Fragment, - private val activeFingers: MutableList, + private val activeFingers: MutableList, ) : FragmentStateAdapter(parent) { override fun createFragment(position: Int): Fragment = FingerFragment.newInstance(activeFingers[position]) @@ -18,7 +18,7 @@ internal class FingerPageAdapter( override fun getItemId(position: Int): Long = activeFingers[position].toItemId() - private fun SampleIdentifier.toItemId(): Long = this.ordinal.toLong() + private fun TemplateIdentifier.toItemId(): Long = this.ordinal.toLong() - private fun Long.itemIdToFingerIdentifier(): SampleIdentifier = SampleIdentifier.values()[this.toInt()] + private fun Long.itemIdToFingerIdentifier(): TemplateIdentifier = TemplateIdentifier.values()[this.toInt()] } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerViewPagerManager.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerViewPagerManager.kt index a4d3e01be6..9605b27ab3 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerViewPagerManager.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerViewPagerManager.kt @@ -7,14 +7,14 @@ import android.widget.LinearLayout import androidx.core.view.children import androidx.fragment.app.Fragment import androidx.viewpager2.widget.ViewPager2 -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.resources.indicatorDrawableId import com.simprints.fingerprint.capture.state.FingerState import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageReports @ExcludedFromGeneratedTestCoverageReports("UI code") internal class FingerViewPagerManager( - private val activeFingers: MutableList, + private val activeFingers: MutableList, private val parentFragment: Fragment, private val viewPager: ViewPager2, private val indicatorLayout: LinearLayout, diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt index aa95b1f67f..927d32343f 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt @@ -2,7 +2,7 @@ package com.simprints.fingerprint.capture.screen import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.tools.time.TimeHelper import com.simprints.fingerprint.capture.screen.FingerprintCaptureViewModelTest.MockAcquireImageResult.OK import com.simprints.fingerprint.capture.screen.FingerprintCaptureViewModelTest.MockCaptureFingerprintResponse.BAD_SCAN @@ -515,11 +515,11 @@ class FingerprintCaptureViewModelTest { coVerify { addBiometricReferenceCreatedEvents.invoke(any(), any()) } vm.finishWithFingerprints.assertEventReceivedWithContentAssertions { actualFingerprints -> - assertThat(actualFingerprints?.samples).hasSize(FOUR_FINGERS_IDS.size) - assertThat(actualFingerprints?.samples?.map { it.identifier }).containsExactlyElementsIn( + assertThat(actualFingerprints?.templates).hasSize(FOUR_FINGERS_IDS.size) + assertThat(actualFingerprints?.templates?.map { it.identifier }).containsExactlyElementsIn( FOUR_FINGERS_IDS, ) - actualFingerprints?.samples?.forEach { + actualFingerprints?.templates?.forEach { assertThat(it.template).isEqualTo(TEMPLATE) } } @@ -573,11 +573,11 @@ class FingerprintCaptureViewModelTest { coVerify { addBiometricReferenceCreatedEvents.invoke(any(), any()) } vm.finishWithFingerprints.assertEventReceivedWithContentAssertions { actualFingerprints -> - assertThat(actualFingerprints?.samples).hasSize(TWO_FINGERS_IDS.size) - assertThat(actualFingerprints?.samples?.map { it.identifier }).containsExactlyElementsIn( + assertThat(actualFingerprints?.templates).hasSize(TWO_FINGERS_IDS.size) + assertThat(actualFingerprints?.templates?.map { it.identifier }).containsExactlyElementsIn( TWO_FINGERS_IDS, ) - actualFingerprints?.samples?.forEach { + actualFingerprints?.templates?.forEach { assertThat(it.template).isEqualTo(TEMPLATE) } } @@ -629,11 +629,11 @@ class FingerprintCaptureViewModelTest { coVerify { addBiometricReferenceCreatedEvents.invoke(any(), any()) } vm.finishWithFingerprints.assertEventReceivedWithContentAssertions { actualFingerprints -> - assertThat(actualFingerprints?.samples).hasSize(TWO_FINGERS_IDS.size) - assertThat(actualFingerprints?.samples?.map { it.identifier }).containsExactlyElementsIn( + assertThat(actualFingerprints?.templates).hasSize(TWO_FINGERS_IDS.size) + assertThat(actualFingerprints?.templates?.map { it.identifier }).containsExactlyElementsIn( TWO_FINGERS_IDS, ) - actualFingerprints?.samples?.forEach { + actualFingerprints?.templates?.forEach { assertThat(it.template).isEqualTo(TEMPLATE) } } @@ -842,13 +842,13 @@ class FingerprintCaptureViewModelTest { coVerify(exactly = 3) { saveFingerprintSampleUseCase.invoke(any(), any(), any(), any()) } vm.finishWithFingerprints.assertEventReceivedWithContentAssertions { actualFingerprints -> - assertThat(actualFingerprints?.samples).hasSize(3) - assertThat(actualFingerprints?.samples?.map { it.identifier }).containsExactly( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_INDEX_FINGER, + assertThat(actualFingerprints?.templates).hasSize(3) + assertThat(actualFingerprints?.templates?.map { it.identifier }).containsExactly( + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_INDEX_FINGER, ) - actualFingerprints?.samples?.forEach { + actualFingerprints?.templates?.forEach { assertThat(it.template).isEqualTo(TEMPLATE) } } @@ -954,13 +954,13 @@ class FingerprintCaptureViewModelTest { vm.handleConfirmFingerprintsAndContinue() vm.finishWithFingerprints.assertEventReceivedWithContentAssertions { actualFingerprints -> - assertThat(actualFingerprints?.samples).hasSize(3) - assertThat(actualFingerprints?.samples?.map { it.identifier }).containsExactly( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_INDEX_FINGER, + assertThat(actualFingerprints?.templates).hasSize(3) + assertThat(actualFingerprints?.templates?.map { it.identifier }).containsExactly( + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_INDEX_FINGER, ) - actualFingerprints?.samples?.forEach { + actualFingerprints?.templates?.forEach { assertThat(it.template).isEqualTo(TEMPLATE) } } @@ -1048,13 +1048,13 @@ class FingerprintCaptureViewModelTest { coVerify(exactly = 3) { saveFingerprintSampleUseCase.invoke(any(), any(), any(), any()) } vm.finishWithFingerprints.assertEventReceivedWithContentAssertions { actualFingerprints -> - assertThat(actualFingerprints?.samples).hasSize(3) - assertThat(actualFingerprints?.samples?.map { it.identifier }).containsExactly( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_THUMB, + assertThat(actualFingerprints?.templates).hasSize(3) + assertThat(actualFingerprints?.templates?.map { it.identifier }).containsExactly( + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_THUMB, ) - actualFingerprints?.samples?.forEach { + actualFingerprints?.templates?.forEach { assertThat(it.template).isEqualTo(TEMPLATE) } } @@ -1525,6 +1525,7 @@ class FingerprintCaptureViewModelTest { fun toCaptureFingerprintResponse(): Any = when (this) { GOOD_SCAN -> AcquireFingerprintTemplateResponse(TEMPLATE, TEMPLATE_FORMAT, GOOD_QUALITY) + DIFFERENT_GOOD_SCAN -> AcquireFingerprintTemplateResponse( DIFFERENT_TEMPLATE, TEMPLATE_FORMAT, @@ -1532,13 +1533,14 @@ class FingerprintCaptureViewModelTest { ) BAD_SCAN -> AcquireFingerprintTemplateResponse(TEMPLATE, TEMPLATE_FORMAT, BAD_QUALITY) + NO_FINGER_DETECTED -> NoFingerDetectedException("No finger detected") + DISCONNECTED -> ScannerDisconnectedException() + UNKNOWN_ERROR -> Error("Oops!") - NEVER_RETURNS -> { - // runBlocking { delay(Duration.INFINITE) } - Error("Nothing to return!") - } + + NEVER_RETURNS -> Error("Nothing to return!") } } @@ -1550,14 +1552,14 @@ class FingerprintCaptureViewModelTest { companion object { val TWO_FINGERS_IDS = listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, ) val FOUR_FINGERS_IDS = listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_INDEX_FINGER, ) const val GOOD_QUALITY = 80 diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt index fa1a5e40d5..f4383ca020 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt @@ -1,6 +1,6 @@ package com.simprints.fingerprint.capture.usecase -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.core.tools.utils.EncodingUtils @@ -43,7 +43,7 @@ internal class AddCaptureEventsUseCaseTest { fun `Saves only capture event when not collected state`() = runTest { useCase.invoke( Timestamp(1L), - FingerState(SampleIdentifier.LEFT_THUMB, listOf(CaptureState.NotCollected)), + FingerState(TemplateIdentifier.LEFT_THUMB, listOf(CaptureState.NotCollected)), 10, false, ) @@ -57,7 +57,7 @@ internal class AddCaptureEventsUseCaseTest { useCase.invoke( Timestamp(1L), FingerState( - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, listOf( CaptureState.ScanProcess.Collected( numberOfBadScans = 0, @@ -79,7 +79,7 @@ internal class AddCaptureEventsUseCaseTest { useCase.invoke( Timestamp(1L), FingerState( - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, listOf( CaptureState.ScanProcess.Collected( numberOfBadScans = 0, @@ -103,7 +103,7 @@ internal class AddCaptureEventsUseCaseTest { useCase.invoke( Timestamp(1L), FingerState( - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, listOf( CaptureState.ScanProcess.Collected( numberOfBadScans = 0, diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCaseTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCaseTest.kt index da264b64d7..43282c76ff 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCaseTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetNextFingerToAddUseCaseTest.kt @@ -1,7 +1,7 @@ package com.simprints.fingerprint.capture.usecase import com.google.common.truth.Truth.assertThat -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import org.junit.Test class GetNextFingerToAddUseCaseTest { @@ -9,7 +9,7 @@ class GetNextFingerToAddUseCaseTest { @Test fun `Returns left thumb as first in priority`() { - assertThat(useCase(listOf())).isEqualTo(SampleIdentifier.LEFT_THUMB) + assertThat(useCase(listOf())).isEqualTo(TemplateIdentifier.LEFT_THUMB) } @Test @@ -17,10 +17,10 @@ class GetNextFingerToAddUseCaseTest { assertThat( useCase( listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, ), ), - ).isEqualTo(SampleIdentifier.RIGHT_THUMB) + ).isEqualTo(TemplateIdentifier.RIGHT_THUMB) } } diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCaseTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCaseTest.kt index 428783f073..667c4a7dd9 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCaseTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/GetStartStateUseCaseTest.kt @@ -1,7 +1,7 @@ package com.simprints.fingerprint.capture.usecase import com.google.common.truth.Truth.assertThat -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.state.CaptureState import com.simprints.fingerprint.capture.state.FingerState import org.junit.Test @@ -14,16 +14,16 @@ internal class GetStartStateUseCaseTest { assertThat( getStartStateUseCase( listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, ), ), ).containsExactlyElementsIn( listOf( - FingerState(SampleIdentifier.LEFT_THUMB, listOf(CaptureState.NotCollected)), - FingerState(SampleIdentifier.RIGHT_5TH_FINGER, listOf(CaptureState.NotCollected)), - FingerState(SampleIdentifier.LEFT_INDEX_FINGER, listOf(CaptureState.NotCollected)), + FingerState(TemplateIdentifier.LEFT_THUMB, listOf(CaptureState.NotCollected)), + FingerState(TemplateIdentifier.RIGHT_5TH_FINGER, listOf(CaptureState.NotCollected)), + FingerState(TemplateIdentifier.LEFT_INDEX_FINGER, listOf(CaptureState.NotCollected)), ), ) } @@ -33,22 +33,22 @@ internal class GetStartStateUseCaseTest { assertThat( getStartStateUseCase( listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, ), ), ).containsExactlyElementsIn( listOf( FingerState( - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, listOf(CaptureState.NotCollected, CaptureState.NotCollected, CaptureState.NotCollected), ), - FingerState(SampleIdentifier.RIGHT_5TH_FINGER, listOf(CaptureState.NotCollected)), - FingerState(SampleIdentifier.LEFT_INDEX_FINGER, listOf(CaptureState.NotCollected, CaptureState.NotCollected)), + FingerState(TemplateIdentifier.RIGHT_5TH_FINGER, listOf(CaptureState.NotCollected)), + FingerState(TemplateIdentifier.LEFT_INDEX_FINGER, listOf(CaptureState.NotCollected, CaptureState.NotCollected)), ), ) } @@ -58,22 +58,22 @@ internal class GetStartStateUseCaseTest { assertThat( getStartStateUseCase( listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.RIGHT_5TH_FINGER, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, ), ), ).containsExactlyElementsIn( listOf( FingerState( - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, listOf(CaptureState.NotCollected, CaptureState.NotCollected, CaptureState.NotCollected), ), - FingerState(SampleIdentifier.RIGHT_5TH_FINGER, listOf(CaptureState.NotCollected)), - FingerState(SampleIdentifier.LEFT_INDEX_FINGER, listOf(CaptureState.NotCollected, CaptureState.NotCollected)), + FingerState(TemplateIdentifier.RIGHT_5TH_FINGER, listOf(CaptureState.NotCollected)), + FingerState(TemplateIdentifier.LEFT_INDEX_FINGER, listOf(CaptureState.NotCollected, CaptureState.NotCollected)), ), ) } diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCaseTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCaseTest.kt index eb1153e29d..0886a355ac 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCaseTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveFingerprintSampleUseCaseTest.kt @@ -2,7 +2,7 @@ package com.simprints.fingerprint.capture.usecase import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.capture.state.CaptureState import com.simprints.fingerprint.capture.state.ScanResult import com.simprints.fingerprint.infra.scanner.v2.scanner.ScannerInfo @@ -44,7 +44,7 @@ class SaveFingerprintSampleUseCaseTest { fun `Returns null if no scan image`() = runTest { val result = useCase.invoke( vero2Configuration, - SampleIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, "captureEventId", createCollectedStub(null), ) @@ -55,7 +55,7 @@ class SaveFingerprintSampleUseCaseTest { fun `Returns null if no capture event id`() = runTest { val result = useCase.invoke( vero2Configuration, - SampleIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, null, createCollectedStub(byteArrayOf()), ) @@ -67,7 +67,7 @@ class SaveFingerprintSampleUseCaseTest { val scannerId = "scannerId" val un20SerialNumber = "un20SerialNumber" val expectedMetadata = mapOf( - "finger" to SampleIdentifier.LEFT_3RD_FINGER.name, + "finger" to TemplateIdentifier.LEFT_3RD_FINGER.name, "dpi" to "1300", "scannerID" to scannerId, "un20SerialNumber" to un20SerialNumber, @@ -94,7 +94,7 @@ class SaveFingerprintSampleUseCaseTest { assertThat( useCase.invoke( vero2Configuration, - SampleIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, "captureEventId", createCollectedStub(byteArrayOf()), ), @@ -110,7 +110,7 @@ class SaveFingerprintSampleUseCaseTest { assertThat( useCase.invoke( vero2Configuration, - SampleIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, "captureEventId", createCollectedStub(byteArrayOf()), ), @@ -130,7 +130,7 @@ class SaveFingerprintSampleUseCaseTest { assertThat( useCase.invoke( vero2Configuration, - SampleIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, "captureEventId", createCollectedStub(byteArrayOf()), ), diff --git a/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/FingerprintBioSdk.kt b/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/FingerprintBioSdk.kt index 56226b4bf2..8c8b23fb57 100644 --- a/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/FingerprintBioSdk.kt +++ b/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/FingerprintBioSdk.kt @@ -1,7 +1,7 @@ package com.simprints.fingerprint.infra.basebiosdk -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.basebiosdk.acquisition.FingerprintImageProvider import com.simprints.fingerprint.infra.basebiosdk.acquisition.FingerprintTemplateProvider import com.simprints.fingerprint.infra.basebiosdk.initialization.SdkInitializer @@ -25,10 +25,10 @@ class FingerprintBioSdk, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, matchingSettings: MatcherSettings?, - ) = fingerprintMatcher.match(probe, candidates, matchingSettings) + ) = fingerprintMatcher.match(probeReference, candidates, matchingSettings) val supportedTemplateFormat: String get() = fingerprintMatcher.supportedTemplateFormat diff --git a/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/matching/FingerprintMatcher.kt b/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/matching/FingerprintMatcher.kt index 87bc9b697a..9d5cb2101a 100644 --- a/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/matching/FingerprintMatcher.kt +++ b/fingerprint/infra/base-bio-sdk/src/main/java/com/simprints/fingerprint/infra/basebiosdk/matching/FingerprintMatcher.kt @@ -1,21 +1,21 @@ package com.simprints.fingerprint.infra.basebiosdk.matching -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.CandidateRecord interface FingerprintMatcher { /** * Matches a [probe] against the given flow of [candidates] - * producing a flow of [MatchComparisonResult]. + * producing a flow of [ComparisonResult]. * * @throws IllegalArgumentException if the TemplateFormats of the supplied [probe] */ suspend fun match( - probe: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, settings: MatcherSettings?, - ): List + ): List val supportedTemplateFormat: String val matcherName: String diff --git a/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/BioSdkWrapper.kt b/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/BioSdkWrapper.kt index 1b044d0d22..31bb39728d 100644 --- a/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/BioSdkWrapper.kt +++ b/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/BioSdkWrapper.kt @@ -1,8 +1,8 @@ package com.simprints.fingerprint.infra.biosdk -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.scanner.domain.fingerprint.AcquireFingerprintImageResponse import com.simprints.fingerprint.infra.scanner.domain.fingerprint.AcquireFingerprintTemplateResponse @@ -26,10 +26,10 @@ interface BioSdkWrapper { suspend fun initialize() suspend fun match( - probe: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, isCrossFingerMatchingEnabled: Boolean, - ): List + ): List suspend fun acquireFingerprintTemplate( capturingResolution: Int?, diff --git a/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapper.kt b/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapper.kt index 08f8f643a6..5ed4bd6810 100644 --- a/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapper.kt +++ b/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapper.kt @@ -1,8 +1,8 @@ package com.simprints.fingerprint.infra.biosdk -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.basebiosdk.FingerprintBioSdk import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.TemplateResponse import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.toDomain @@ -35,10 +35,10 @@ class NECBioSdkWrapper @Inject constructor( override suspend fun initialize() = bioSdk.initialize() override suspend fun match( - probe: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, isCrossFingerMatchingEnabled: Boolean, - ): List = bioSdk.match(probe, candidates, NecMatchingSettings(isCrossFingerMatchingEnabled)) + ): List = bioSdk.match(probeReference, candidates, NecMatchingSettings(isCrossFingerMatchingEnabled)) override suspend fun acquireFingerprintTemplate( capturingResolution: Int?, diff --git a/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapper.kt b/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapper.kt index da205a1177..ef623a6d16 100644 --- a/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapper.kt +++ b/fingerprint/infra/bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapper.kt @@ -1,7 +1,7 @@ package com.simprints.fingerprint.infra.biosdk -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.basebiosdk.FingerprintBioSdk import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.TemplateResponse import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.toDomain @@ -36,10 +36,10 @@ class SimprintsBioSdkWrapper @Inject constructor( } override suspend fun match( - probe: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, isCrossFingerMatchingEnabled: Boolean, - ) = bioSdk.match(probe, candidates, SimAfisMatcherSettings(isCrossFingerMatchingEnabled)) + ) = bioSdk.match(probeReference, candidates, SimAfisMatcherSettings(isCrossFingerMatchingEnabled)) override suspend fun acquireFingerprintTemplate( capturingResolution: Int?, diff --git a/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapperTest.kt b/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapperTest.kt index cda1652cbe..ef9089ff6e 100644 --- a/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapperTest.kt +++ b/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/NECBioSdkWrapperTest.kt @@ -1,8 +1,8 @@ package com.simprints.fingerprint.infra.biosdk import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.basebiosdk.FingerprintBioSdk import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.ImageResponse import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.TemplateResponse @@ -69,8 +69,8 @@ class NECBioSdkWrapperTest { @Test fun `calls match on bio sdk`() = runTest { // Given - val probe = listOf(mockk()) - val candidates = listOf(mockk()) + val probe = mockk() + val candidates = listOf(mockk()) val isCrossFingerMatchingEnabled = true val settings = NecMatchingSettings(isCrossFingerMatchingEnabled) // When diff --git a/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapperTest.kt b/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapperTest.kt index 586167658b..17d84e339c 100644 --- a/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapperTest.kt +++ b/fingerprint/infra/bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdk/SimprintsBioSdkWrapperTest.kt @@ -1,8 +1,8 @@ package com.simprints.fingerprint.infra.biosdk import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.basebiosdk.FingerprintBioSdk import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.ImageResponse import com.simprints.fingerprint.infra.basebiosdk.acquisition.domain.TemplateResponse @@ -63,8 +63,8 @@ class SimprintsBioSdkWrapperTest { @Test fun `Calls match on bio sdk`() = runTest { // Given - val probe = listOf(mockk()) - val candidates = listOf(mockk()) + val probe = mockk() + val candidates = listOf(mockk()) val isCrossFingerMatchingEnabled = true val settings = SimAfisMatcherSettings(isCrossFingerMatchingEnabled) // When diff --git a/fingerprint/infra/nec-bio-sdk/src/main/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImpl.kt b/fingerprint/infra/nec-bio-sdk/src/main/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImpl.kt index 5680a0f02a..b86dc1ea13 100644 --- a/fingerprint/infra/nec-bio-sdk/src/main/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImpl.kt +++ b/fingerprint/infra/nec-bio-sdk/src/main/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImpl.kt @@ -1,10 +1,10 @@ package com.simprints.fingerprint.infra.necsdkimpl.matching -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.basebiosdk.exceptions.BioSdkException import com.simprints.fingerprint.infra.basebiosdk.matching.FingerprintMatcher import com.simprints.fingerprint.infra.necsdkimpl.acquisition.template.NEC_TEMPLATE_FORMAT @@ -19,24 +19,26 @@ internal class FingerprintMatcherImpl @Inject constructor( override val matcherName: String = "NEC" override suspend fun match( - probe: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, settings: NecMatchingSettings?, - ): List { + ): List { // if probe template format is not supported by NEC matcher, return empty list - if (probe.templateFormatNotSupportedByNecMatcher()) { + if (probeReference.templateFormatNotSupportedByNecMatcher()) { return emptyList() } + val probeTemplates = probeReference.templates + return if (settings?.crossFingerComparison == true) { - crossFingerMatching(probe, candidates) + crossFingerMatching(probeTemplates, candidates) } else { - sameFingerMatching(probe, candidates) + sameFingerMatching(probeTemplates, candidates) } } private fun sameFingerMatching( - probe: List, - candidates: List, + probe: List, + candidates: List, ) = candidates.map { sameFingerMatching(probe, it) } @@ -47,27 +49,29 @@ internal class FingerprintMatcherImpl @Inject constructor( * - Getting the matching score for each similar finger pairs using NEC SDK * - The overall score is the average of the individual finger match scores * - We ignore probe fingers that doesn't have matching candidate fingers - * @param probe + * @param probeTemplates * @param candidate * @return MatchResult */ private fun sameFingerMatching( - probe: List, - candidate: Identity, - ): MatchComparisonResult { + probeTemplates: List, + candidate: CandidateRecord, + ): ComparisonResult { var fingers = 0 // the number of fingers used in matching - val total = probe.sumOf { fingerprint -> - candidate.templateForFinger(fingerprint.identifier)?.let { candidateTemplate -> + val candidateTemplates = candidate.references.flatMap { it.templates } + + val total = probeTemplates.sumOf { probeTemplate -> + candidateTemplates.find { it.identifier == probeTemplate.identifier }?.let { candidateTemplate -> fingers++ - verify(fingerprint, candidateTemplate) + verify(probeTemplate, candidateTemplate) } ?: 0.toDouble() } - return MatchComparisonResult(candidate.subjectId, getOverallScore(total, fingers)) + return ComparisonResult(candidate.subjectId, getOverallScore(total, fingers)) } private fun verify( - probe: CaptureSample, - candidate: Sample, + probe: BiometricTemplateCapture, + candidate: BiometricTemplate, ) = try { nec .match( @@ -79,31 +83,28 @@ internal class FingerprintMatcherImpl @Inject constructor( } private fun crossFingerMatching( - probe: List, - candidates: List, + probe: List, + candidates: List, ) = candidates.map { crossFingerMatching(probe, it) } private fun crossFingerMatching( - probe: List, - candidate: Identity, - ): MatchComparisonResult { + probe: List, + candidate: CandidateRecord, + ): ComparisonResult { // Number of fingers used in matching val fingers = probe.size + val candidateTemplates = candidate.references.flatMap { it.templates } // Sum of maximum matching score for each finger val total = probe.sumOf { probeTemplate -> - candidate.samples.maxOf { candidateTemplate -> - verify(probeTemplate, candidateTemplate) - } + candidateTemplates.maxOf { candidateTemplate -> verify(probeTemplate, candidateTemplate) } } // Matching score = total/number of fingers - return MatchComparisonResult(candidate.subjectId, getOverallScore(total, fingers)) + return ComparisonResult(candidate.subjectId, getOverallScore(total, fingers)) } - private fun Identity.templateForFinger(fingerId: SampleIdentifier) = samples.find { it.identifier == fingerId } - - private fun CaptureSample.toNecTemplate() = NECTemplate(template, 0) // Quality score not used + private fun BiometricTemplate.toNecTemplate() = NECTemplate(template, 0) // Quality score not used - private fun Sample.toNecTemplate() = NECTemplate(template, 0) // Quality score not used + private fun BiometricTemplateCapture.toNecTemplate() = NECTemplate(template, 0) // Quality score not used private fun getOverallScore( total: Double, @@ -115,4 +116,4 @@ internal class FingerprintMatcherImpl @Inject constructor( } } -private fun List.templateFormatNotSupportedByNecMatcher(): Boolean = any { it.format != NEC_TEMPLATE_FORMAT } +private fun BiometricReferenceCapture.templateFormatNotSupportedByNecMatcher(): Boolean = format != NEC_TEMPLATE_FORMAT diff --git a/fingerprint/infra/nec-bio-sdk/src/test/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImplTest.kt b/fingerprint/infra/nec-bio-sdk/src/test/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImplTest.kt index 4f79c54341..6d75335bc0 100644 --- a/fingerprint/infra/nec-bio-sdk/src/test/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImplTest.kt +++ b/fingerprint/infra/nec-bio-sdk/src/test/java/com/simprints/fingerprint/infra/necsdkimpl/matching/FingerprintMatcherImplTest.kt @@ -2,10 +2,12 @@ package com.simprints.fingerprint.infra.necsdkimpl.matching import com.google.common.truth.* import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.reference.CandidateRecord +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.infra.basebiosdk.exceptions.BioSdkException import com.simprints.fingerprint.infra.necsdkimpl.acquisition.template.NEC_TEMPLATE_FORMAT import com.simprints.necwrapper.nec.NEC @@ -31,56 +33,56 @@ class FingerprintMatcherImplTest { fun `test match FingerprintIdentities with the same fingerprint IDs`() = runTest { // Given every { nec.match(any(), any(), any()) } returns 3 - val probe = generateProbe(SampleIdentifier.LEFT_THUMB, SampleIdentifier.RIGHT_THUMB) + val probe = generateProbe(TemplateIdentifier.LEFT_THUMB, TemplateIdentifier.RIGHT_THUMB) val candidates = listOf( - generateIdentity(SampleIdentifier.LEFT_THUMB, SampleIdentifier.RIGHT_THUMB), - generateIdentity(SampleIdentifier.LEFT_THUMB, SampleIdentifier.RIGHT_THUMB), - generateIdentity(SampleIdentifier.LEFT_THUMB, SampleIdentifier.RIGHT_THUMB), + generateIdentity(TemplateIdentifier.LEFT_THUMB, TemplateIdentifier.RIGHT_THUMB), + generateIdentity(TemplateIdentifier.LEFT_THUMB, TemplateIdentifier.RIGHT_THUMB), + generateIdentity(TemplateIdentifier.LEFT_THUMB, TemplateIdentifier.RIGHT_THUMB), ) // When val result = matcher.match(probe, candidates, NecMatchingSettings(false)) // Then Truth.assertThat(result.size).isEqualTo(3) - Truth.assertThat(result[0].confidence).isEqualTo(3) + Truth.assertThat(result[0].comparisonScore).isEqualTo(3) } @Test fun `test match FingerprintIdentities with different fingerprint IDs`() = runTest { // Given every { nec.match(any(), any(), any()) } returns 3 - val probe = generateProbe(SampleIdentifier.LEFT_INDEX_FINGER, SampleIdentifier.RIGHT_INDEX_FINGER) - val candidate = generateIdentity(SampleIdentifier.LEFT_THUMB, SampleIdentifier.RIGHT_THUMB) + val probe = generateProbe(TemplateIdentifier.LEFT_INDEX_FINGER, TemplateIdentifier.RIGHT_INDEX_FINGER) + val candidate = generateIdentity(TemplateIdentifier.LEFT_THUMB, TemplateIdentifier.RIGHT_THUMB) // When val result = matcher.match(probe, listOf(candidate), NecMatchingSettings(false)) // Then Truth.assertThat(result.size).isEqualTo(1) - Truth.assertThat(result[0].confidence).isEqualTo(0) + Truth.assertThat(result[0].comparisonScore).isEqualTo(0) } @Test fun `test match FingerprintIdentities with different fingerprint IDs and crossFingerComparison`() = runTest { // Given every { nec.match(any(), any(), any()) } returns 3 - val probe = generateProbe(SampleIdentifier.LEFT_INDEX_FINGER, SampleIdentifier.RIGHT_INDEX_FINGER) - val candidate = generateIdentity(SampleIdentifier.LEFT_THUMB, SampleIdentifier.RIGHT_THUMB) + val probe = generateProbe(TemplateIdentifier.LEFT_INDEX_FINGER, TemplateIdentifier.RIGHT_INDEX_FINGER) + val candidate = generateIdentity(TemplateIdentifier.LEFT_THUMB, TemplateIdentifier.RIGHT_THUMB) // When val result = matcher.match(probe, listOf(candidate), NecMatchingSettings(true)) // Then Truth.assertThat(result.size).isEqualTo(1) - Truth.assertThat(result[0].confidence).isEqualTo(3) + Truth.assertThat(result[0].comparisonScore).isEqualTo(3) } @Test fun `test match FingerprintIdentities with only one equal fingerprint IDs`() = runTest { // Given every { nec.match(any(), any(), any()) } returns 3 - val probe = generateProbe(SampleIdentifier.LEFT_INDEX_FINGER, SampleIdentifier.RIGHT_INDEX_FINGER) - val candidate = generateIdentity(SampleIdentifier.LEFT_INDEX_FINGER, SampleIdentifier.RIGHT_THUMB) + val probe = generateProbe(TemplateIdentifier.LEFT_INDEX_FINGER, TemplateIdentifier.RIGHT_INDEX_FINGER) + val candidate = generateIdentity(TemplateIdentifier.LEFT_INDEX_FINGER, TemplateIdentifier.RIGHT_THUMB) // When val result = matcher.match(probe, listOf(candidate), NecMatchingSettings(false)) // Then Truth.assertThat(result.size).isEqualTo(1) - Truth.assertThat(result[0].confidence).isEqualTo(3) + Truth.assertThat(result[0].comparisonScore).isEqualTo(3) } @Test(expected = BioSdkException.TemplateMatchingException::class) @@ -90,10 +92,10 @@ class FingerprintMatcherImplTest { nec.match(any(), any(), any()) } throws NEC.AttemptedToRunBeforeInitializedException() val probe = generateProbe( - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, ) - val candidate = generateIdentity(SampleIdentifier.LEFT_INDEX_FINGER, SampleIdentifier.RIGHT_THUMB) + val candidate = generateIdentity(TemplateIdentifier.LEFT_INDEX_FINGER, TemplateIdentifier.RIGHT_THUMB) // When matcher.match(probe, listOf(candidate), NecMatchingSettings(false)) } @@ -103,12 +105,12 @@ class FingerprintMatcherImplTest { // Given val probe = generateProbe( - SampleIdentifier.LEFT_INDEX_FINGER, - SampleIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.RIGHT_INDEX_FINGER, format = "Unsupported", ) val candidate = - generateIdentity(SampleIdentifier.LEFT_THUMB, SampleIdentifier.RIGHT_THUMB) + generateIdentity(TemplateIdentifier.LEFT_THUMB, TemplateIdentifier.RIGHT_THUMB) // When val result = matcher.match(probe, listOf(candidate), NecMatchingSettings(false)) // Then @@ -116,30 +118,37 @@ class FingerprintMatcherImplTest { } private fun generateProbe( - vararg fingers: SampleIdentifier, + vararg fingers: TemplateIdentifier, format: String = NEC_TEMPLATE_FORMAT, - ) = fingers.map { - CaptureSample( - captureEventId = it.name, - identifier = it, - modality = Modality.FINGERPRINT, - format = format, - template = ByteArray(0), - ) - } + ) = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = format, + templates = fingers.map { + BiometricTemplateCapture( + captureEventId = it.name, + template = ByteArray(0), + identifier = it, + ) + }, + ) private fun generateIdentity( - vararg fingers: SampleIdentifier, + vararg fingers: TemplateIdentifier, format: String = NEC_TEMPLATE_FORMAT, - ) = Identity( + ) = CandidateRecord( subjectId = "id", - samples = fingers.map { - Sample( + references = fingers.map { + BiometricReference( referenceId = it.name, - identifier = it, modality = Modality.FINGERPRINT, format = format, - template = ByteArray(0), + templates = listOf( + BiometricTemplate( + template = ByteArray(0), + identifier = it, + ), + ), ) }, ) diff --git a/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImpl.kt b/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImpl.kt index 67ee2a925b..967fd34d8e 100644 --- a/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImpl.kt +++ b/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImpl.kt @@ -1,8 +1,8 @@ package com.simprints.fingerprint.infra.biosdkimpl.matching -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.basebiosdk.matching.FingerprintMatcher import com.simprints.fingerprint.infra.biosdkimpl.matching.SimAfisMatcher.Companion.SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT import javax.inject.Inject @@ -14,8 +14,12 @@ internal class FingerprintMatcherImpl @Inject constructor( override val matcherName: String = "SIM_AFIS" override suspend fun match( - probe: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, settings: SimAfisMatcherSettings?, - ): List = simAfisMatcher.match(probe, candidates, settings?.crossFingerComparison ?: false) + ): List = simAfisMatcher.match( + probeReference = probeReference, + candidates = candidates, + crossFingerComparison = settings?.crossFingerComparison ?: false, + ) } diff --git a/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcher.kt b/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcher.kt index 50264ac57e..d4ad580fb3 100644 --- a/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcher.kt +++ b/fingerprint/infra/simprints-bio-sdk/src/main/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcher.kt @@ -1,11 +1,12 @@ package com.simprints.fingerprint.infra.biosdkimpl.matching import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.common.TemplateIdentifier +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.fingerprint.infra.simafiswrapper.JNILibAfisInterface import com.simprints.fingerprint.infra.simafiswrapper.models.SimAfisFingerIdentifier import com.simprints.fingerprint.infra.simafiswrapper.models.SimAfisFingerprint @@ -26,25 +27,27 @@ internal class SimAfisMatcher @Inject constructor( private val jniLibAfis: JNILibAfisInterface, ) { fun match( - probe: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, crossFingerComparison: Boolean, - ): List { + ): List { // if probe template format is not supported by SimAfisMatcher, return empty list - if (probe.templateFormatNotSupportedBySimAfisMatcher()) { + if (probeReference.templateFormatNotSupportedBySimAfisMatcher()) { return emptyList() } + val probeTemplates = probeReference.templates + return if (crossFingerComparison) { - crossFingerMatch(probe, candidates) + crossFingerMatch(probeTemplates, candidates) } else { - match(probe, candidates) + match(probeTemplates, candidates) } } private fun match( - probe: List, - candidates: List, - ): List { + probe: List, + candidates: List, + ): List { val simAfisCandidates = candidates.map { it.toSimAfisPerson() } println("Matching ${simAfisCandidates.size} candidates using all ${jniLibAfis.getNbCores()} cores") @@ -56,37 +59,39 @@ internal class SimAfisMatcher @Inject constructor( ) return results.zip(simAfisCandidates).map { (score, candidate) -> - MatchComparisonResult(candidate.guid, score) + ComparisonResult(candidate.guid, score) } } - private fun Identity.toSimAfisPerson(): SimAfisPerson = SimAfisPerson(subjectId, samples.map { it.toSimAfisFingerprint() }) + private fun CandidateRecord.toSimAfisPerson(): SimAfisPerson = + SimAfisPerson(subjectId, references.flatMap { it.templates }.map { it.toSimAfisFingerprint() }) - private fun Sample.toSimAfisFingerprint(): SimAfisFingerprint = SimAfisFingerprint(identifier.toSimAfisFingerIdentifier(), template) + private fun BiometricTemplate.toSimAfisFingerprint(): SimAfisFingerprint = + SimAfisFingerprint(identifier.toSimAfisFingerIdentifier(), template) - private fun List.toSimAfisPerson(): SimAfisPerson = SimAfisPerson("", map { it.toSimAfisFingerprint() }) + private fun List.toSimAfisPerson(): SimAfisPerson = SimAfisPerson("", map { it.toSimAfisFingerprint() }) - private fun CaptureSample.toSimAfisFingerprint(): SimAfisFingerprint = + private fun BiometricTemplateCapture.toSimAfisFingerprint(): SimAfisFingerprint = SimAfisFingerprint(identifier.toSimAfisFingerIdentifier(), template) @ExcludedFromGeneratedTestCoverageReports(reason = "This is just a mapping function") - private fun SampleIdentifier.toSimAfisFingerIdentifier(): SimAfisFingerIdentifier = when (this) { - SampleIdentifier.RIGHT_5TH_FINGER -> SimAfisFingerIdentifier.RIGHT_5TH_FINGER - SampleIdentifier.RIGHT_4TH_FINGER -> SimAfisFingerIdentifier.RIGHT_4TH_FINGER - SampleIdentifier.RIGHT_3RD_FINGER -> SimAfisFingerIdentifier.RIGHT_3RD_FINGER - SampleIdentifier.RIGHT_INDEX_FINGER -> SimAfisFingerIdentifier.RIGHT_INDEX_FINGER - SampleIdentifier.RIGHT_THUMB -> SimAfisFingerIdentifier.RIGHT_THUMB - SampleIdentifier.LEFT_THUMB -> SimAfisFingerIdentifier.LEFT_THUMB - SampleIdentifier.LEFT_INDEX_FINGER -> SimAfisFingerIdentifier.LEFT_INDEX_FINGER - SampleIdentifier.LEFT_3RD_FINGER -> SimAfisFingerIdentifier.LEFT_3RD_FINGER - SampleIdentifier.LEFT_4TH_FINGER -> SimAfisFingerIdentifier.LEFT_4TH_FINGER - SampleIdentifier.LEFT_5TH_FINGER -> SimAfisFingerIdentifier.LEFT_5TH_FINGER - SampleIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") + private fun TemplateIdentifier.toSimAfisFingerIdentifier(): SimAfisFingerIdentifier = when (this) { + TemplateIdentifier.RIGHT_5TH_FINGER -> SimAfisFingerIdentifier.RIGHT_5TH_FINGER + TemplateIdentifier.RIGHT_4TH_FINGER -> SimAfisFingerIdentifier.RIGHT_4TH_FINGER + TemplateIdentifier.RIGHT_3RD_FINGER -> SimAfisFingerIdentifier.RIGHT_3RD_FINGER + TemplateIdentifier.RIGHT_INDEX_FINGER -> SimAfisFingerIdentifier.RIGHT_INDEX_FINGER + TemplateIdentifier.RIGHT_THUMB -> SimAfisFingerIdentifier.RIGHT_THUMB + TemplateIdentifier.LEFT_THUMB -> SimAfisFingerIdentifier.LEFT_THUMB + TemplateIdentifier.LEFT_INDEX_FINGER -> SimAfisFingerIdentifier.LEFT_INDEX_FINGER + TemplateIdentifier.LEFT_3RD_FINGER -> SimAfisFingerIdentifier.LEFT_3RD_FINGER + TemplateIdentifier.LEFT_4TH_FINGER -> SimAfisFingerIdentifier.LEFT_4TH_FINGER + TemplateIdentifier.LEFT_5TH_FINGER -> SimAfisFingerIdentifier.LEFT_5TH_FINGER + TemplateIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") } private fun crossFingerMatch( - probe: List, - candidates: List, + probe: List, + candidates: List, ) = candidates.map { crossFingerMatching(probe, it, jniLibAfis) } /** @@ -98,14 +103,14 @@ internal class SimAfisMatcher @Inject constructor( * @return MatchResult */ private fun crossFingerMatching( - probe: List, - candidate: Identity, + probe: List, + candidate: CandidateRecord, jniLibAfis: JNILibAfisInterface, - ): MatchComparisonResult { - // Number of fingers used in matching - val fingers = probe.fingerprintsTemplates.size + ): ComparisonResult { + // Fingers used in matching + val fingers = probe.fingerprintsTemplates // Sum of maximum matching score for each finger - val total = probe.fingerprintsTemplates + val total = fingers .sumOf { probeTemplate -> candidate.fingerprintsTemplates .maxOf { candidateTemplate -> @@ -116,7 +121,7 @@ internal class SimAfisMatcher @Inject constructor( }.toDouble() } // Matching score = total/number of fingers - return MatchComparisonResult(candidate.subjectId, getOverallScore(total, fingers)) + return ComparisonResult(candidate.subjectId, getOverallScore(total, fingers.size)) } private fun getOverallScore( @@ -133,13 +138,13 @@ internal class SimAfisMatcher @Inject constructor( } } -val List.fingerprintsTemplates +val List.fingerprintsTemplates get() = map { it.template.toByteBuffer() } -val Identity.fingerprintsTemplates - get() = samples.map { it.template.toByteBuffer() } +val CandidateRecord.fingerprintsTemplates + get() = references.flatMap { it.templates }.map { it.template.toByteBuffer() } private fun ByteArray.toByteBuffer(): ByteBuffer = ByteBuffer.allocateDirect(size).put(this) -fun List.templateFormatNotSupportedBySimAfisMatcher(): Boolean = - any { it.format != SimAfisMatcher.SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT } +fun BiometricReferenceCapture.templateFormatNotSupportedBySimAfisMatcher(): Boolean = + format != SimAfisMatcher.SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT diff --git a/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImplTest.kt b/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImplTest.kt index 6513abc896..8a5d2edb0d 100644 --- a/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImplTest.kt +++ b/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/FingerprintMatcherImplTest.kt @@ -1,9 +1,9 @@ package com.simprints.fingerprint.infra.biosdkimpl.matching import com.google.common.truth.* -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.CandidateRecord import io.mockk.* import kotlinx.coroutines.test.runTest import org.junit.Test @@ -15,11 +15,11 @@ class FingerprintMatcherImplTest { fun match() = runTest { // Given val matcher = FingerprintMatcherImpl(simAfisMatcher) - val probe = listOf(mockk()) - val candidates: List = mockk() + val probe = mockk() + val candidates: List = mockk() val simAfisMatcherSettings = SimAfisMatcherSettings() simAfisMatcherSettings.crossFingerComparison = false - val matchResult: List = mockk() + val matchResult: List = mockk() every { simAfisMatcher.match(probe, candidates, false) } returns matchResult diff --git a/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcherTest.kt b/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcherTest.kt index 6469c01377..e541003b70 100644 --- a/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcherTest.kt +++ b/fingerprint/infra/simprints-bio-sdk/src/test/java/com/simprints/fingerprint/infra/biosdkimpl/matching/SimAfisMatcherTest.kt @@ -2,10 +2,12 @@ package com.simprints.fingerprint.infra.biosdkimpl.matching import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.reference.CandidateRecord +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.fingerprint.infra.scanner.v2.tools.primitives.byteArrayOf import com.simprints.fingerprint.infra.simafiswrapper.JNILibAfisInterface import io.mockk.* @@ -32,22 +34,29 @@ class SimAfisMatcherTest { fun `test same finger match`() = runTest { every { jniLibAfis.identify(any(), any(), 1) } returns floatArrayOf(1F) - val probes = listOf( - CaptureSample( - captureEventId = "referenceId", - identifier = SampleIdentifier.RIGHT_THUMB, - template = IsoFingerprintTemplateGenerator.generate(1), - format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, - modality = Modality.FINGERPRINT, + val probes = BiometricReferenceCapture( + referenceId = "referenceId", + format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, + modality = Modality.FINGERPRINT, + templates = listOf( + BiometricTemplateCapture( + captureEventId = "referenceId", + identifier = TemplateIdentifier.RIGHT_THUMB, + template = IsoFingerprintTemplateGenerator.generate(1), + ), ), ) - val candidate = Identity( + val candidate = CandidateRecord( "candidate", listOf( - Sample( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.RIGHT_THUMB, - template = IsoFingerprintTemplateGenerator.generate(1), + templates = listOf( + BiometricTemplate( + template = IsoFingerprintTemplateGenerator.generate(1), + identifier = TemplateIdentifier.RIGHT_THUMB, + ), + ), format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, modality = Modality.FINGERPRINT, ), @@ -57,28 +66,35 @@ class SimAfisMatcherTest { val result = simAfisMatcher.match(probes, listOf(candidate), false).last() // Then verify { jniLibAfis.identify(any(), any(), any()) } - assertThat(result.confidence).isEqualTo(1) + assertThat(result.comparisonScore).isEqualTo(1) } @Test fun `test matching probe with other template format ignore candidate`() = runTest { every { jniLibAfis.identify(any(), any(), 1) } returns floatArrayOf(1F) - val probes = listOf( - CaptureSample( - captureEventId = "referenceId", - identifier = SampleIdentifier.RIGHT_3RD_FINGER, - template = IsoFingerprintTemplateGenerator.generate(1), - format = "NEC_1", - modality = Modality.FINGERPRINT, + val probes = BiometricReferenceCapture( + referenceId = "referenceId", + format = "NEC_1", + modality = Modality.FINGERPRINT, + templates = listOf( + BiometricTemplateCapture( + captureEventId = "referenceId", + identifier = TemplateIdentifier.RIGHT_3RD_FINGER, + template = IsoFingerprintTemplateGenerator.generate(1), + ), ), ) - val candidate = Identity( + val candidate = CandidateRecord( "candidate", listOf( - Sample( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.RIGHT_3RD_FINGER, - template = IsoFingerprintTemplateGenerator.generate(1), + templates = listOf( + BiometricTemplate( + template = IsoFingerprintTemplateGenerator.generate(1), + identifier = TemplateIdentifier.RIGHT_3RD_FINGER, + ), + ), format = "NEC_1", modality = Modality.FINGERPRINT, ), @@ -96,56 +112,73 @@ class SimAfisMatcherTest { val template2 = byteArrayOf(2) val template3 = byteArrayOf(3) - val probe = listOf( - CaptureSample( - captureEventId = "referenceId", - identifier = SampleIdentifier.RIGHT_THUMB, - template = template1, - format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, - modality = Modality.FINGERPRINT, - ), - CaptureSample( - captureEventId = "referenceId", - identifier = SampleIdentifier.LEFT_THUMB, - template = template2, - format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, - modality = Modality.FINGERPRINT, + val probe = BiometricReferenceCapture( + referenceId = "referenceId", + format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, + modality = Modality.FINGERPRINT, + templates = listOf( + BiometricTemplateCapture( + captureEventId = "referenceId", + identifier = TemplateIdentifier.RIGHT_THUMB, + template = template1, + ), + BiometricTemplateCapture( + captureEventId = "referenceId", + identifier = TemplateIdentifier.LEFT_THUMB, + template = template2, + ), ), ) - val candidate1 = Identity( + val candidate1 = CandidateRecord( subjectId = "candidate1", - samples = listOf( - Sample( + references = listOf( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.LEFT_4TH_FINGER, - template = template2, + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.LEFT_4TH_FINGER, + template = template2, + ), + ), format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, modality = Modality.FINGERPRINT, ), - Sample( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.LEFT_5TH_FINGER, - template = template1, + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.LEFT_5TH_FINGER, + template = template1, + ), + ), format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, modality = Modality.FINGERPRINT, ), ), ) - val candidate2 = Identity( + val candidate2 = CandidateRecord( subjectId = "candidate2", - samples = listOf( - Sample( + references = listOf( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.RIGHT_3RD_FINGER, - template = template3, + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.RIGHT_3RD_FINGER, + template = template3, + ), + ), format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, modality = Modality.FINGERPRINT, ), - Sample( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.RIGHT_5TH_FINGER, - template = template1, + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.RIGHT_5TH_FINGER, + template = template1, + ), + ), format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, modality = Modality.FINGERPRINT, ), @@ -161,8 +194,8 @@ class SimAfisMatcherTest { listOf(candidate1, candidate2), true, ) - val maxScore = matchingResult.maxOf { it.confidence } - val minScore = matchingResult.minOf { it.confidence } + val maxScore = matchingResult.maxOf { it.comparisonScore } + val minScore = matchingResult.minOf { it.comparisonScore } // Then verify(exactly = 8) { jniLibAfis.verify(any(), any()) } assertThat(maxScore).isEqualTo(1) @@ -173,21 +206,34 @@ class SimAfisMatcherTest { fun `test crossFingerMatching zero fingers success`() { // Given every { jniLibAfis.verify(any(), any()) } returns 1F - val probes = listOf() - val candidate = Identity( + val probes = BiometricReferenceCapture( + referenceId = "referenceId", + format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, + modality = Modality.FINGERPRINT, + templates = emptyList(), + ) + val candidate = CandidateRecord( "candidate", listOf( - Sample( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.LEFT_THUMB, - template = IsoFingerprintTemplateGenerator.generate(1), + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.LEFT_THUMB, + template = IsoFingerprintTemplateGenerator.generate(1), + ), + ), format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, modality = Modality.FINGERPRINT, ), - Sample( + BiometricReference( referenceId = "referenceId", - identifier = SampleIdentifier.LEFT_3RD_FINGER, - template = IsoFingerprintTemplateGenerator.generate(1), + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.LEFT_3RD_FINGER, + template = IsoFingerprintTemplateGenerator.generate(1), + ), + ), format = SIMAFIS_MATCHER_SUPPORTED_TEMPLATE_FORMAT, modality = Modality.FINGERPRINT, ), @@ -198,7 +244,7 @@ class SimAfisMatcherTest { val result = simAfisMatcher.match(probes, listOf(candidate), true) // Then verify(exactly = 0) { jniLibAfis.verify(any(), any()) } - assertThat(result[0].confidence).isEqualTo(0) + assertThat(result[0].comparisonScore).isEqualTo(0) } companion object { diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt index 9c64dc6380..c310e9cce5 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt @@ -2,7 +2,7 @@ package com.simprints.infra.config.store.local import androidx.datastore.core.DataStore import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.tools.utils.LanguageHelper import com.simprints.infra.config.store.AbsolutePath @@ -185,8 +185,8 @@ internal class ConfigLocalDataSourceImpl @Inject constructor( allowedSDKs = listOf(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), secugenSimMatcher = FingerprintConfiguration.FingerprintSdkConfiguration( fingersToCapture = listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, ), decisionPolicy = DecisionPolicy( 0, @@ -247,7 +247,7 @@ internal class ConfigLocalDataSourceImpl @Inject constructor( ), ), custom = null, - multifactorId = null + multifactorId = null, ).toProto() val defaultDeviceConfiguration: ProtoDeviceConfiguration = DeviceConfiguration( language = "", diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt index 3b4fa1bb21..f49fe591d1 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt @@ -3,7 +3,7 @@ package com.simprints.infra.config.store.local.migrations.models import androidx.annotation.Keep import com.fasterxml.jackson.annotation.JsonProperty import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.json.JsonHelper import com.simprints.infra.config.store.models.ConsentConfiguration @@ -135,10 +135,10 @@ internal data class OldProjectConfig( secugenSimMatcher = FingerprintConfiguration.FingerprintSdkConfiguration( fingersToCapture = fingerprintsToCollect ?.split(",") - ?.map { SampleIdentifier.valueOf(it) } + ?.map { TemplateIdentifier.valueOf(it) } ?: listOf( - SampleIdentifier.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER, ), decisionPolicy = fingerprintConfidenceThresholds?.let { parseDecisionPolicy(it) } ?: DecisionPolicy(0, 0, 700), diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/Finger.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/Finger.kt index ba7f9311aa..0368243a99 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/Finger.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/Finger.kt @@ -1,31 +1,31 @@ package com.simprints.infra.config.store.local.models -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier -internal fun SampleIdentifier.toProtoFinger(): ProtoFinger = when (this) { - SampleIdentifier.LEFT_THUMB -> ProtoFinger.LEFT_THUMB - SampleIdentifier.LEFT_INDEX_FINGER -> ProtoFinger.LEFT_INDEX_FINGER - SampleIdentifier.LEFT_3RD_FINGER -> ProtoFinger.LEFT_3RD_FINGER - SampleIdentifier.LEFT_4TH_FINGER -> ProtoFinger.LEFT_4TH_FINGER - SampleIdentifier.LEFT_5TH_FINGER -> ProtoFinger.LEFT_5TH_FINGER - SampleIdentifier.RIGHT_THUMB -> ProtoFinger.RIGHT_THUMB - SampleIdentifier.RIGHT_INDEX_FINGER -> ProtoFinger.RIGHT_INDEX_FINGER - SampleIdentifier.RIGHT_3RD_FINGER -> ProtoFinger.RIGHT_3RD_FINGER - SampleIdentifier.RIGHT_4TH_FINGER -> ProtoFinger.RIGHT_4TH_FINGER - SampleIdentifier.RIGHT_5TH_FINGER -> ProtoFinger.RIGHT_5TH_FINGER +internal fun TemplateIdentifier.toProtoFinger(): ProtoFinger = when (this) { + TemplateIdentifier.LEFT_THUMB -> ProtoFinger.LEFT_THUMB + TemplateIdentifier.LEFT_INDEX_FINGER -> ProtoFinger.LEFT_INDEX_FINGER + TemplateIdentifier.LEFT_3RD_FINGER -> ProtoFinger.LEFT_3RD_FINGER + TemplateIdentifier.LEFT_4TH_FINGER -> ProtoFinger.LEFT_4TH_FINGER + TemplateIdentifier.LEFT_5TH_FINGER -> ProtoFinger.LEFT_5TH_FINGER + TemplateIdentifier.RIGHT_THUMB -> ProtoFinger.RIGHT_THUMB + TemplateIdentifier.RIGHT_INDEX_FINGER -> ProtoFinger.RIGHT_INDEX_FINGER + TemplateIdentifier.RIGHT_3RD_FINGER -> ProtoFinger.RIGHT_3RD_FINGER + TemplateIdentifier.RIGHT_4TH_FINGER -> ProtoFinger.RIGHT_4TH_FINGER + TemplateIdentifier.RIGHT_5TH_FINGER -> ProtoFinger.RIGHT_5TH_FINGER else -> ProtoFinger.UNRECOGNIZED } -internal fun ProtoFinger.toDomain(): SampleIdentifier = when (this) { - ProtoFinger.UNRECOGNIZED -> SampleIdentifier.NONE - ProtoFinger.LEFT_THUMB -> SampleIdentifier.LEFT_THUMB - ProtoFinger.LEFT_INDEX_FINGER -> SampleIdentifier.LEFT_INDEX_FINGER - ProtoFinger.LEFT_3RD_FINGER -> SampleIdentifier.LEFT_3RD_FINGER - ProtoFinger.LEFT_4TH_FINGER -> SampleIdentifier.LEFT_4TH_FINGER - ProtoFinger.LEFT_5TH_FINGER -> SampleIdentifier.LEFT_5TH_FINGER - ProtoFinger.RIGHT_THUMB -> SampleIdentifier.RIGHT_THUMB - ProtoFinger.RIGHT_INDEX_FINGER -> SampleIdentifier.RIGHT_INDEX_FINGER - ProtoFinger.RIGHT_3RD_FINGER -> SampleIdentifier.RIGHT_3RD_FINGER - ProtoFinger.RIGHT_4TH_FINGER -> SampleIdentifier.RIGHT_4TH_FINGER - ProtoFinger.RIGHT_5TH_FINGER -> SampleIdentifier.RIGHT_5TH_FINGER +internal fun ProtoFinger.toDomain(): TemplateIdentifier = when (this) { + ProtoFinger.UNRECOGNIZED -> TemplateIdentifier.NONE + ProtoFinger.LEFT_THUMB -> TemplateIdentifier.LEFT_THUMB + ProtoFinger.LEFT_INDEX_FINGER -> TemplateIdentifier.LEFT_INDEX_FINGER + ProtoFinger.LEFT_3RD_FINGER -> TemplateIdentifier.LEFT_3RD_FINGER + ProtoFinger.LEFT_4TH_FINGER -> TemplateIdentifier.LEFT_4TH_FINGER + ProtoFinger.LEFT_5TH_FINGER -> TemplateIdentifier.LEFT_5TH_FINGER + ProtoFinger.RIGHT_THUMB -> TemplateIdentifier.RIGHT_THUMB + ProtoFinger.RIGHT_INDEX_FINGER -> TemplateIdentifier.RIGHT_INDEX_FINGER + ProtoFinger.RIGHT_3RD_FINGER -> TemplateIdentifier.RIGHT_3RD_FINGER + ProtoFinger.RIGHT_4TH_FINGER -> TemplateIdentifier.RIGHT_4TH_FINGER + ProtoFinger.RIGHT_5TH_FINGER -> TemplateIdentifier.RIGHT_5TH_FINGER } diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt index 81dbc7dbdc..076ed84f4c 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt @@ -2,7 +2,7 @@ package com.simprints.infra.config.store.models import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.ModalitySdkType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier data class FingerprintConfiguration( val allowedScanners: List, @@ -12,7 +12,7 @@ data class FingerprintConfiguration( val nec: FingerprintSdkConfiguration?, ) { data class FingerprintSdkConfiguration( - val fingersToCapture: List, + val fingersToCapture: List, override val decisionPolicy: DecisionPolicy, val comparisonStrategyForVerification: FingerComparisonStrategy, val vero1: Vero1Configuration? = null, diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt index 42c13773f0..02475839d4 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt @@ -2,7 +2,7 @@ package com.simprints.infra.config.store.remote.models import androidx.annotation.Keep import com.simprints.core.domain.common.AgeGroup -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.store.models.FingerprintConfiguration @Keep @@ -59,16 +59,16 @@ internal data class ApiFingerprintConfiguration( ; fun toDomain() = when (this) { - LEFT_THUMB -> SampleIdentifier.LEFT_THUMB - LEFT_INDEX_FINGER -> SampleIdentifier.LEFT_INDEX_FINGER - LEFT_3RD_FINGER -> SampleIdentifier.LEFT_3RD_FINGER - LEFT_4TH_FINGER -> SampleIdentifier.LEFT_4TH_FINGER - LEFT_5TH_FINGER -> SampleIdentifier.LEFT_5TH_FINGER - RIGHT_THUMB -> SampleIdentifier.RIGHT_THUMB - RIGHT_INDEX_FINGER -> SampleIdentifier.RIGHT_INDEX_FINGER - RIGHT_3RD_FINGER -> SampleIdentifier.RIGHT_3RD_FINGER - RIGHT_4TH_FINGER -> SampleIdentifier.RIGHT_4TH_FINGER - RIGHT_5TH_FINGER -> SampleIdentifier.RIGHT_5TH_FINGER + LEFT_THUMB -> TemplateIdentifier.LEFT_THUMB + LEFT_INDEX_FINGER -> TemplateIdentifier.LEFT_INDEX_FINGER + LEFT_3RD_FINGER -> TemplateIdentifier.LEFT_3RD_FINGER + LEFT_4TH_FINGER -> TemplateIdentifier.LEFT_4TH_FINGER + LEFT_5TH_FINGER -> TemplateIdentifier.LEFT_5TH_FINGER + RIGHT_THUMB -> TemplateIdentifier.RIGHT_THUMB + RIGHT_INDEX_FINGER -> TemplateIdentifier.RIGHT_INDEX_FINGER + RIGHT_3RD_FINGER -> TemplateIdentifier.RIGHT_3RD_FINGER + RIGHT_4TH_FINGER -> TemplateIdentifier.RIGHT_4TH_FINGER + RIGHT_5TH_FINGER -> TemplateIdentifier.RIGHT_5TH_FINGER } } diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/local/models/FingerprintConfigurationTest.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/local/models/FingerprintConfigurationTest.kt index 614501df9a..94caba87c9 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/local/models/FingerprintConfigurationTest.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/local/models/FingerprintConfigurationTest.kt @@ -2,7 +2,7 @@ package com.simprints.infra.config.store.local.models import com.google.common.truth.Truth.* import com.simprints.core.domain.common.AgeGroup -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.testtools.fingerprintConfiguration import com.simprints.infra.config.store.testtools.protoFingerprintConfiguration @@ -44,16 +44,16 @@ class FingerprintConfigurationTest { @Test fun `should map correctly the Finger enums`() { val mapping = mapOf( - ProtoFinger.LEFT_THUMB to SampleIdentifier.LEFT_THUMB, - ProtoFinger.LEFT_INDEX_FINGER to SampleIdentifier.LEFT_INDEX_FINGER, - ProtoFinger.LEFT_3RD_FINGER to SampleIdentifier.LEFT_3RD_FINGER, - ProtoFinger.LEFT_4TH_FINGER to SampleIdentifier.LEFT_4TH_FINGER, - ProtoFinger.LEFT_5TH_FINGER to SampleIdentifier.LEFT_5TH_FINGER, - ProtoFinger.RIGHT_THUMB to SampleIdentifier.RIGHT_THUMB, - ProtoFinger.RIGHT_INDEX_FINGER to SampleIdentifier.RIGHT_INDEX_FINGER, - ProtoFinger.RIGHT_3RD_FINGER to SampleIdentifier.RIGHT_3RD_FINGER, - ProtoFinger.RIGHT_4TH_FINGER to SampleIdentifier.RIGHT_4TH_FINGER, - ProtoFinger.RIGHT_5TH_FINGER to SampleIdentifier.RIGHT_5TH_FINGER, + ProtoFinger.LEFT_THUMB to TemplateIdentifier.LEFT_THUMB, + ProtoFinger.LEFT_INDEX_FINGER to TemplateIdentifier.LEFT_INDEX_FINGER, + ProtoFinger.LEFT_3RD_FINGER to TemplateIdentifier.LEFT_3RD_FINGER, + ProtoFinger.LEFT_4TH_FINGER to TemplateIdentifier.LEFT_4TH_FINGER, + ProtoFinger.LEFT_5TH_FINGER to TemplateIdentifier.LEFT_5TH_FINGER, + ProtoFinger.RIGHT_THUMB to TemplateIdentifier.RIGHT_THUMB, + ProtoFinger.RIGHT_INDEX_FINGER to TemplateIdentifier.RIGHT_INDEX_FINGER, + ProtoFinger.RIGHT_3RD_FINGER to TemplateIdentifier.RIGHT_3RD_FINGER, + ProtoFinger.RIGHT_4TH_FINGER to TemplateIdentifier.RIGHT_4TH_FINGER, + ProtoFinger.RIGHT_5TH_FINGER to TemplateIdentifier.RIGHT_5TH_FINGER, ) mapping.forEach { diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt index 0a3eef1a55..27ebfb0087 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt @@ -1,7 +1,7 @@ package com.simprints.infra.config.store.models import com.google.common.truth.* -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import org.junit.Test class FingerprintConfigurationTest { @@ -12,7 +12,7 @@ class FingerprintConfigurationTest { allowedSDKs = listOf(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), displayHandIcons = true, secugenSimMatcher = FingerprintConfiguration.FingerprintSdkConfiguration( - fingersToCapture = listOf(SampleIdentifier.LEFT_THUMB), + fingersToCapture = listOf(TemplateIdentifier.LEFT_THUMB), decisionPolicy = DecisionPolicy(20, 50, 100), comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(60), @@ -34,7 +34,7 @@ class FingerprintConfigurationTest { displayHandIcons = true, secugenSimMatcher = null, nec = FingerprintConfiguration.FingerprintSdkConfiguration( - fingersToCapture = listOf(SampleIdentifier.LEFT_THUMB), + fingersToCapture = listOf(TemplateIdentifier.LEFT_THUMB), decisionPolicy = DecisionPolicy(20, 50, 100), comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(60), diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt index d56b56e080..9e3713ff27 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt @@ -2,7 +2,7 @@ package com.simprints.infra.config.store.remote.models import com.google.common.truth.Truth.* import com.simprints.core.domain.common.AgeGroup -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.models.MaxCaptureAttempts import com.simprints.infra.config.store.models.Vero1Configuration @@ -56,7 +56,7 @@ class ApiFingerprintConfigurationTest { listOf(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), true, FingerprintConfiguration.FingerprintSdkConfiguration( - fingersToCapture = listOf(SampleIdentifier.LEFT_3RD_FINGER), + fingersToCapture = listOf(TemplateIdentifier.LEFT_3RD_FINGER), decisionPolicy = decisionPolicy, comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = null, @@ -94,7 +94,7 @@ class ApiFingerprintConfigurationTest { true, null, FingerprintConfiguration.FingerprintSdkConfiguration( - fingersToCapture = listOf(SampleIdentifier.LEFT_3RD_FINGER), + fingersToCapture = listOf(TemplateIdentifier.LEFT_3RD_FINGER), decisionPolicy = decisionPolicy, comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(10), @@ -111,16 +111,16 @@ class ApiFingerprintConfigurationTest { @Test fun `should map correctly the Finger enums`() { val mapping = mapOf( - ApiFingerprintConfiguration.ApiFinger.LEFT_THUMB to SampleIdentifier.LEFT_THUMB, - ApiFingerprintConfiguration.ApiFinger.LEFT_INDEX_FINGER to SampleIdentifier.LEFT_INDEX_FINGER, - ApiFingerprintConfiguration.ApiFinger.LEFT_3RD_FINGER to SampleIdentifier.LEFT_3RD_FINGER, - ApiFingerprintConfiguration.ApiFinger.LEFT_4TH_FINGER to SampleIdentifier.LEFT_4TH_FINGER, - ApiFingerprintConfiguration.ApiFinger.LEFT_5TH_FINGER to SampleIdentifier.LEFT_5TH_FINGER, - ApiFingerprintConfiguration.ApiFinger.RIGHT_THUMB to SampleIdentifier.RIGHT_THUMB, - ApiFingerprintConfiguration.ApiFinger.RIGHT_INDEX_FINGER to SampleIdentifier.RIGHT_INDEX_FINGER, - ApiFingerprintConfiguration.ApiFinger.RIGHT_3RD_FINGER to SampleIdentifier.RIGHT_3RD_FINGER, - ApiFingerprintConfiguration.ApiFinger.RIGHT_4TH_FINGER to SampleIdentifier.RIGHT_4TH_FINGER, - ApiFingerprintConfiguration.ApiFinger.RIGHT_5TH_FINGER to SampleIdentifier.RIGHT_5TH_FINGER, + ApiFingerprintConfiguration.ApiFinger.LEFT_THUMB to TemplateIdentifier.LEFT_THUMB, + ApiFingerprintConfiguration.ApiFinger.LEFT_INDEX_FINGER to TemplateIdentifier.LEFT_INDEX_FINGER, + ApiFingerprintConfiguration.ApiFinger.LEFT_3RD_FINGER to TemplateIdentifier.LEFT_3RD_FINGER, + ApiFingerprintConfiguration.ApiFinger.LEFT_4TH_FINGER to TemplateIdentifier.LEFT_4TH_FINGER, + ApiFingerprintConfiguration.ApiFinger.LEFT_5TH_FINGER to TemplateIdentifier.LEFT_5TH_FINGER, + ApiFingerprintConfiguration.ApiFinger.RIGHT_THUMB to TemplateIdentifier.RIGHT_THUMB, + ApiFingerprintConfiguration.ApiFinger.RIGHT_INDEX_FINGER to TemplateIdentifier.RIGHT_INDEX_FINGER, + ApiFingerprintConfiguration.ApiFinger.RIGHT_3RD_FINGER to TemplateIdentifier.RIGHT_3RD_FINGER, + ApiFingerprintConfiguration.ApiFinger.RIGHT_4TH_FINGER to TemplateIdentifier.RIGHT_4TH_FINGER, + ApiFingerprintConfiguration.ApiFinger.RIGHT_5TH_FINGER to TemplateIdentifier.RIGHT_5TH_FINGER, ) mapping.forEach { diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt index c9521af088..71aa590a3c 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt @@ -3,7 +3,7 @@ package com.simprints.infra.config.store.testtools import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.infra.config.store.local.models.ProtoAllowedAgeRange import com.simprints.infra.config.store.local.models.ProtoConsentConfiguration @@ -269,7 +269,7 @@ internal val apiFingerprintConfiguration = ApiFingerprintConfiguration( ) internal val fingerprintSdkConfiguration = FingerprintConfiguration.FingerprintSdkConfiguration( - fingersToCapture = listOf(SampleIdentifier.LEFT_3RD_FINGER), + fingersToCapture = listOf(TemplateIdentifier.LEFT_3RD_FINGER), decisionPolicy = decisionPolicy, comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(qualityThreshold = 10), diff --git a/infra/core/src/main/java/com/simprints/core/domain/capture/BiometricReferenceCapture.kt b/infra/core/src/main/java/com/simprints/core/domain/capture/BiometricReferenceCapture.kt new file mode 100644 index 0000000000..fc2a946536 --- /dev/null +++ b/infra/core/src/main/java/com/simprints/core/domain/capture/BiometricReferenceCapture.kt @@ -0,0 +1,19 @@ +package com.simprints.core.domain.capture + +import android.os.Parcelable +import androidx.annotation.Keep +import com.simprints.core.domain.common.Modality +import com.simprints.core.domain.step.StepParams +import com.simprints.core.domain.step.StepResult +import kotlinx.parcelize.Parcelize + +@Keep +@Parcelize +data class BiometricReferenceCapture( + val referenceId: String, + val modality: Modality, + val format: String, + var templates: List, +) : StepResult, + StepParams, + Parcelable diff --git a/infra/core/src/main/java/com/simprints/core/domain/sample/CaptureSample.kt b/infra/core/src/main/java/com/simprints/core/domain/capture/BiometricTemplateCapture.kt similarity index 61% rename from infra/core/src/main/java/com/simprints/core/domain/sample/CaptureSample.kt rename to infra/core/src/main/java/com/simprints/core/domain/capture/BiometricTemplateCapture.kt index 28b5d533ff..e7c969c717 100644 --- a/infra/core/src/main/java/com/simprints/core/domain/sample/CaptureSample.kt +++ b/infra/core/src/main/java/com/simprints/core/domain/capture/BiometricTemplateCapture.kt @@ -1,40 +1,36 @@ -package com.simprints.core.domain.sample +package com.simprints.core.domain.capture import android.os.Parcelable import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.step.StepResult import kotlinx.parcelize.Parcelize -import java.util.UUID @Parcelize @ExcludedFromGeneratedTestCoverageReports("Data class with generated code") -data class CaptureSample( +data class BiometricTemplateCapture( val captureEventId: String, - val modality: Modality, - val format: String, val template: ByteArray, - val identifier: SampleIdentifier = SampleIdentifier.NONE, -) : StepResult, - StepParams, + val identifier: TemplateIdentifier = TemplateIdentifier.NONE, +) : StepParams, + StepResult, Parcelable { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - other as CaptureSample + other as BiometricTemplateCapture - if (identifier != other.identifier) return false if (!template.contentEquals(other.template)) return false + if (identifier != other.identifier) return false return true } override fun hashCode(): Int { - var result = identifier.hashCode() - result = 31 * result + template.contentHashCode() + var result = template.contentHashCode() + result = 31 * result + identifier.hashCode() return result } } diff --git a/infra/core/src/main/java/com/simprints/core/domain/sample/SampleIdentifier.kt b/infra/core/src/main/java/com/simprints/core/domain/common/TemplateIdentifier.kt similarity index 85% rename from infra/core/src/main/java/com/simprints/core/domain/sample/SampleIdentifier.kt rename to infra/core/src/main/java/com/simprints/core/domain/common/TemplateIdentifier.kt index 2082a60101..5899673a5a 100644 --- a/infra/core/src/main/java/com/simprints/core/domain/sample/SampleIdentifier.kt +++ b/infra/core/src/main/java/com/simprints/core/domain/common/TemplateIdentifier.kt @@ -1,11 +1,11 @@ -package com.simprints.core.domain.sample +package com.simprints.core.domain.common import androidx.annotation.Keep import com.simprints.core.ExcludedFromGeneratedTestCoverageReports @Keep @ExcludedFromGeneratedTestCoverageReports("Enum") -enum class SampleIdentifier { +enum class TemplateIdentifier { NONE, // Fingerprint specific identifiers diff --git a/infra/core/src/main/java/com/simprints/core/domain/sample/MatchComparisonResult.kt b/infra/core/src/main/java/com/simprints/core/domain/comparison/ComparisonResult.kt similarity index 73% rename from infra/core/src/main/java/com/simprints/core/domain/sample/MatchComparisonResult.kt rename to infra/core/src/main/java/com/simprints/core/domain/comparison/ComparisonResult.kt index 17964472a7..a474cb0273 100644 --- a/infra/core/src/main/java/com/simprints/core/domain/sample/MatchComparisonResult.kt +++ b/infra/core/src/main/java/com/simprints/core/domain/comparison/ComparisonResult.kt @@ -1,4 +1,4 @@ -package com.simprints.core.domain.sample +package com.simprints.core.domain.comparison import android.os.Parcelable import androidx.annotation.Keep @@ -8,9 +8,9 @@ import kotlinx.parcelize.Parcelize @Keep @Parcelize -data class MatchComparisonResult( +data class ComparisonResult( val subjectId: String, - val confidence: Float, + val comparisonScore: Float, ) : StepParams, StepResult, Parcelable diff --git a/infra/core/src/main/java/com/simprints/core/domain/reference/BiometricReference.kt b/infra/core/src/main/java/com/simprints/core/domain/reference/BiometricReference.kt new file mode 100644 index 0000000000..6a55024fba --- /dev/null +++ b/infra/core/src/main/java/com/simprints/core/domain/reference/BiometricReference.kt @@ -0,0 +1,15 @@ +package com.simprints.core.domain.reference + +import android.os.Parcelable +import com.simprints.core.ExcludedFromGeneratedTestCoverageReports +import com.simprints.core.domain.common.Modality +import kotlinx.parcelize.Parcelize + +@Parcelize +@ExcludedFromGeneratedTestCoverageReports("Data class with generated code") +data class BiometricReference( + val referenceId: String, + val modality: Modality, + val format: String, + val templates: List, +) : Parcelable diff --git a/infra/core/src/main/java/com/simprints/core/domain/sample/Sample.kt b/infra/core/src/main/java/com/simprints/core/domain/reference/BiometricTemplate.kt similarity index 53% rename from infra/core/src/main/java/com/simprints/core/domain/sample/Sample.kt rename to infra/core/src/main/java/com/simprints/core/domain/reference/BiometricTemplate.kt index b6f54799dd..30cc3778b1 100644 --- a/infra/core/src/main/java/com/simprints/core/domain/sample/Sample.kt +++ b/infra/core/src/main/java/com/simprints/core/domain/reference/BiometricTemplate.kt @@ -1,37 +1,38 @@ -package com.simprints.core.domain.sample +package com.simprints.core.domain.reference import android.os.Parcelable import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier +import com.simprints.core.domain.step.StepParams +import com.simprints.core.domain.step.StepResult import kotlinx.parcelize.Parcelize import java.util.UUID @Parcelize @ExcludedFromGeneratedTestCoverageReports("Data class with generated code") -data class Sample( +data class BiometricTemplate( val id: String = UUID.randomUUID().toString(), - val identifier: SampleIdentifier = SampleIdentifier.NONE, - val modality: Modality, - val referenceId: String, - val format: String, val template: ByteArray, -) : Parcelable { + val identifier: TemplateIdentifier = TemplateIdentifier.NONE, +) : StepParams, + StepResult, + Parcelable { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - other as Sample + other as BiometricTemplate - if (identifier != other.identifier) return false + // ID is explicitly ignored in the equality check since it is randomly generated on creation and used only locally if (!template.contentEquals(other.template)) return false + if (identifier != other.identifier) return false return true } override fun hashCode(): Int { - var result = identifier.hashCode() - result = 31 * result + template.contentHashCode() + var result = template.contentHashCode() + result = 31 * result + identifier.hashCode() return result } } diff --git a/infra/core/src/main/java/com/simprints/core/domain/sample/Identity.kt b/infra/core/src/main/java/com/simprints/core/domain/reference/CandidateRecord.kt similarity index 51% rename from infra/core/src/main/java/com/simprints/core/domain/sample/Identity.kt rename to infra/core/src/main/java/com/simprints/core/domain/reference/CandidateRecord.kt index 5bc5af9e5f..06e5261e95 100644 --- a/infra/core/src/main/java/com/simprints/core/domain/sample/Identity.kt +++ b/infra/core/src/main/java/com/simprints/core/domain/reference/CandidateRecord.kt @@ -1,10 +1,10 @@ -package com.simprints.core.domain.sample +package com.simprints.core.domain.reference import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -class Identity( +class CandidateRecord( val subjectId: String, - val samples: List, + val references: List, ) : Parcelable diff --git a/infra/core/src/main/java/com/simprints/core/domain/sample/CaptureIdentity.kt b/infra/core/src/main/java/com/simprints/core/domain/sample/CaptureIdentity.kt deleted file mode 100644 index cc6ef358f8..0000000000 --- a/infra/core/src/main/java/com/simprints/core/domain/sample/CaptureIdentity.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.simprints.core.domain.sample - -import androidx.annotation.Keep -import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.step.StepResult - -@Keep -data class CaptureIdentity( - val referenceId: String, - val modality: Modality, - var samples: List, -) : StepResult diff --git a/infra/enrolment-records/repository/src/androidTest/kotlin/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceIntegrationTest.kt b/infra/enrolment-records/repository/src/androidTest/kotlin/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceIntegrationTest.kt index 7f4aa067a3..f88b7489ab 100644 --- a/infra/enrolment-records/repository/src/androidTest/kotlin/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceIntegrationTest.kt +++ b/infra/enrolment-records/repository/src/androidTest/kotlin/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceIntegrationTest.kt @@ -5,8 +5,9 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.time.TimeHelper @@ -18,10 +19,10 @@ import com.simprints.infra.enrolment.records.realm.store.RealmWrapper import com.simprints.infra.enrolment.records.realm.store.RealmWrapperImpl import com.simprints.infra.enrolment.records.realm.store.config.RealmConfig import com.simprints.infra.enrolment.records.realm.store.models.DbSubject -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.security.SecurityManager import com.simprints.infra.security.keyprovider.LocalDbKey import dagger.hilt.android.testing.HiltAndroidRule @@ -100,7 +101,7 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { // Given val subjectId = UUID.randomUUID().toString() val subject = createTestSubject(subjectId) - val creationAction = SubjectAction.Creation(subject) + val creationAction = EnrolmentRecordAction.Creation(subject) val project = mockk() // When @@ -124,9 +125,9 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { // Given val subjectId = UUID.randomUUID().toString() val subject = createTestSubject(subjectId) - dataSource.performActions(listOf(SubjectAction.Creation(subject)), mockk()) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(subject)), mockk()) - val query = SubjectQuery(subjectId = subjectId) + val query = EnrolmentRecordQuery(subjectId = subjectId) // When val result = dataSource.load(query) @@ -143,13 +144,13 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { val subject2 = createTestSubject(projectId = "other-project") dataSource.performActions( listOf( - SubjectAction.Creation(subject1), - SubjectAction.Creation(subject2), + EnrolmentRecordAction.Creation(subject1), + EnrolmentRecordAction.Creation(subject2), ), mockk(), ) // Query to match only subject1 - val query = SubjectQuery(projectId = subject1.projectId) + val query = EnrolmentRecordQuery(projectId = subject1.projectId) // When val count = dataSource.count(query, mockk()) @@ -163,19 +164,19 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { // Given val subjectId = UUID.randomUUID().toString() val subject = createTestSubject(subjectId) - dataSource.performActions(listOf(SubjectAction.Creation(subject)), mockk()) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(subject)), mockk()) // Verify it was created - var count = dataSource.count(SubjectQuery(subjectId = subjectId), mockk()) + var count = dataSource.count(EnrolmentRecordQuery(subjectId = subjectId), mockk()) assertThat(count).isEqualTo(1) - val deletionAction = SubjectAction.Deletion(subjectId) + val deletionAction = EnrolmentRecordAction.Deletion(subjectId) // When dataSource.performActions(listOf(deletionAction), mockk()) // Then - count = dataSource.count(SubjectQuery(subjectId = subjectId), mockk()) + count = dataSource.count(EnrolmentRecordQuery(subjectId = subjectId), mockk()) assertThat(count).isEqualTo(0) } @@ -187,23 +188,23 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { val subject3 = createTestSubject(projectId = "proj2") dataSource.performActions( listOf( - SubjectAction.Creation(subject1), - SubjectAction.Creation(subject2), - SubjectAction.Creation(subject3), + EnrolmentRecordAction.Creation(subject1), + EnrolmentRecordAction.Creation(subject2), + EnrolmentRecordAction.Creation(subject3), ), mockk(), ) - val queryToDelete = SubjectQuery(projectId = "proj1") + val queryToDelete = EnrolmentRecordQuery(projectId = "proj1") // When dataSource.delete(listOf(queryToDelete)) // Then - val remainingCount = dataSource.count(SubjectQuery(), mockk()) + val remainingCount = dataSource.count(EnrolmentRecordQuery(), mockk()) assertThat(remainingCount).isEqualTo(1) - val allSubjects = dataSource.load(SubjectQuery()) + val allSubjects = dataSource.load(EnrolmentRecordQuery()) assertThat(allSubjects.first().projectId).isEqualTo("proj2") } @@ -214,8 +215,8 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { val subject2 = createTestSubject() dataSource.performActions( listOf( - SubjectAction.Creation(subject1), - SubjectAction.Creation(subject2), + EnrolmentRecordAction.Creation(subject1), + EnrolmentRecordAction.Creation(subject2), ), mockk(), ) @@ -224,7 +225,7 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { dataSource.deleteAll() // Then - val count = dataSource.count(SubjectQuery(), mockk()) + val count = dataSource.count(EnrolmentRecordQuery(), mockk()) assertThat(count).isEqualTo(0) } @@ -234,9 +235,13 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { // Given val subjectId = UUID.randomUUID().toString() val originalSubject = createTestSubject(subjectId) - originalSubject.samples = listOf( - Sample( - template = byteArrayOf(), + originalSubject.references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(), + ), + ), format = "ISO", referenceId = "ref1", modality = Modality.FACE, @@ -250,22 +255,30 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { type = ExternalCredentialType.NHISCard, ), ) - dataSource.performActions(listOf(SubjectAction.Creation(originalSubject)), mockk()) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(originalSubject)), mockk()) - val updateAction = SubjectAction.Update( + val updateAction = EnrolmentRecordAction.Update( subjectId, samplesToAdd = listOf( - Sample( - template = byteArrayOf(1, 2, 3), + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(1, 2, 3), + ), + ), format = "ISO", referenceId = "ref2", modality = Modality.FACE, ), - Sample( - template = byteArrayOf(4, 5, 6), + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(4, 5, 6), + identifier = TemplateIdentifier.LEFT_THUMB, + ), + ), format = "ISO", referenceId = "ref3", - identifier = SampleIdentifier.LEFT_THUMB, modality = Modality.FINGERPRINT, ), ), @@ -306,13 +319,17 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { } @Test - fun givenManySubjectsWithFaceSamples_whenLoadIdentitiesIsCalledWithRanges_thenReturnsBatchedIdentities() = runTest { + fun givenManySubjectsWithFaceSamples_whenLoadIdentitiesIsCalledWithRanges_thenReturnsBatchedCandidateRecords() = runTest { // Given val subjects = (1..10).map { i -> createTestSubject(subjectId = UUID.randomUUID().toString()).apply { - samples = listOf( - Sample( - template = byteArrayOf(i.toByte()), + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(i.toByte()), + ), + ), format = "ISO", referenceId = "ref$i", modality = Modality.FACE, @@ -321,16 +338,16 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { } } dataSource.performActions( - subjects.map { SubjectAction.Creation(it) }, + subjects.map { EnrolmentRecordAction.Creation(it) }, mockk(), ) - val query = SubjectQuery(format = "ISO") + val query = EnrolmentRecordQuery(format = "ISO") val ranges = listOf(0..2, 3..5, 6..9) // 3 batches val loadedCandidates = mutableListOf() // When - val channel = dataSource.loadIdentities( + val channel = dataSource.loadCandidateRecords( query = query, ranges = ranges, dataSource = mockk(), @@ -339,7 +356,7 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { onCandidateLoaded = { loadedCandidates.add(Unit) }, ) - val results = mutableListOf() + val results = mutableListOf() for (batch in channel) { results.add(batch) } @@ -357,28 +374,32 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { // Given val subjects = (1..10).map { i -> createTestSubject(subjectId = UUID.randomUUID().toString()).apply { - samples = listOf( - Sample( - template = byteArrayOf(i.toByte()), + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(i.toByte()), + identifier = TemplateIdentifier.LEFT_THUMB, + ), + ), format = "ISO", referenceId = "ref$i", - identifier = SampleIdentifier.LEFT_THUMB, modality = Modality.FINGERPRINT, ), ) } } dataSource.performActions( - subjects.map { SubjectAction.Creation(it) }, + subjects.map { EnrolmentRecordAction.Creation(it) }, mockk(), ) - val query = SubjectQuery(format = "ISO") + val query = EnrolmentRecordQuery(format = "ISO") val ranges = listOf(0..2, 3..5, 6..9) // 3 batches val loadedCandidates = mutableListOf() // When - val channel = dataSource.loadIdentities( + val channel = dataSource.loadCandidateRecords( query = query, ranges = ranges, dataSource = mockk(), @@ -387,7 +408,7 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { onCandidateLoaded = { loadedCandidates.add(Unit) }, ) - val results = mutableListOf() + val results = mutableListOf() for (batch in channel) { results.add(batch) } @@ -407,12 +428,12 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { createTestSubject(subjectId = UUID.randomUUID().toString()) } dataSource.performActions( - subjects.map { SubjectAction.Creation(it) }, + subjects.map { EnrolmentRecordAction.Creation(it) }, mockk(), ) val batchSize = 4 - val batches = mutableListOf>() + val batches = mutableListOf>() // When val flow = dataSource.loadAllSubjectsInBatches(batchSize) @@ -433,7 +454,7 @@ class RealmEnrolmentRecordLocalDataSourceIntegrationTest { projectId: String = "test-project", attendantId: String = "test-attendant", moduleId: String = "test-module", - ): Subject = Subject( + ): EnrolmentRecord = EnrolmentRecord( subjectId = subjectId, projectId = projectId, attendantId = attendantId.asTokenizableRaw(), diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/IdentityDataSource.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/CandidateRecordDataSource.kt similarity index 75% rename from infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/IdentityDataSource.kt rename to infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/CandidateRecordDataSource.kt index 068749d68f..33ea42125d 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/IdentityDataSource.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/CandidateRecordDataSource.kt @@ -2,25 +2,25 @@ package com.simprints.infra.enrolment.records.repository import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.ReceiveChannel -interface IdentityDataSource { +interface CandidateRecordDataSource { suspend fun count( - query: SubjectQuery = SubjectQuery(), + query: EnrolmentRecordQuery = EnrolmentRecordQuery(), dataSource: BiometricDataSource = BiometricDataSource.Simprints, ): Int - suspend fun loadIdentities( - query: SubjectQuery, + suspend fun loadCandidateRecords( + query: EnrolmentRecordQuery, ranges: List, dataSource: BiometricDataSource, project: Project, scope: CoroutineScope, onCandidateLoaded: suspend () -> Unit, - ): ReceiveChannel + ): ReceiveChannel /** * Loads identities concurrently using the provided dispatcher and parallelism level. diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepository.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepository.kt index 8684140537..d9bb02af47 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepository.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepository.kt @@ -3,7 +3,7 @@ package com.simprints.infra.enrolment.records.repository import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.local.EnrolmentRecordLocalDataSource @ExcludedFromGeneratedTestCoverageReports("This is an interface with no logic") @@ -13,7 +13,7 @@ interface EnrolmentRecordRepository : EnrolmentRecordLocalDataSource { suspend fun tokenizeExistingRecords(project: Project) override suspend fun count( - query: SubjectQuery, + query: EnrolmentRecordQuery, dataSource: BiometricDataSource, ): Int } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImpl.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImpl.kt index 43a6d5d0d1..51738d9488 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImpl.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImpl.kt @@ -8,9 +8,9 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.enrolment.records.realm.store.exceptions.RealmUninitialisedException import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.local.SelectEnrolmentRecordLocalDataSourceUseCase import com.simprints.infra.enrolment.records.repository.local.migration.InsertRecordsInRoomDuringMigrationUseCase import com.simprints.infra.enrolment.records.repository.remote.EnrolmentRecordRemoteDataSource @@ -25,7 +25,7 @@ import javax.inject.Singleton @Singleton internal class EnrolmentRecordRepositoryImpl @Inject constructor( private val remoteDataSource: EnrolmentRecordRemoteDataSource, - @CommCareDataSource private val commCareDataSource: IdentityDataSource, + @CommCareDataSource private val commCareDataSource: CandidateRecordDataSource, private val tokenizationProcessor: TokenizationProcessor, private val selectEnrolmentRecordLocalDataSource: SelectEnrolmentRecordLocalDataSourceUseCase, @DispatcherIO private val dispatcher: CoroutineDispatcher, @@ -42,9 +42,9 @@ internal class EnrolmentRecordRepositoryImpl @Inject constructor( override suspend fun uploadRecords(subjectIds: List) = withContext(dispatcher) { val lastUploadedRecord = prefs.getString(PROGRESS_KEY, null) - var query = SubjectQuery(sort = true, afterSubjectId = lastUploadedRecord) + var query = EnrolmentRecordQuery(sort = true, afterSubjectId = lastUploadedRecord) if (subjectIds.isNotEmpty()) { - query = SubjectQuery( + query = EnrolmentRecordQuery( subjectIds = subjectIds, sort = true, afterSubjectId = lastUploadedRecord, @@ -62,7 +62,7 @@ internal class EnrolmentRecordRepositoryImpl @Inject constructor( override suspend fun tokenizeExistingRecords(project: Project) { try { - val query = SubjectQuery(projectId = project.id, hasUntokenizedFields = true) + val query = EnrolmentRecordQuery(projectId = project.id, hasUntokenizedFields = true) val tokenizedSubjectsCreateAction = selectEnrolmentRecordLocalDataSource() .load(query) .mapNotNull { subject -> @@ -78,11 +78,13 @@ internal class EnrolmentRecordRepositoryImpl @Inject constructor( project = project, ) return@mapNotNull subject.copy(moduleId = moduleId, attendantId = attendantId) - }.map(SubjectAction::Creation) + }.map(EnrolmentRecordAction::Creation) selectEnrolmentRecordLocalDataSource().performActions(tokenizedSubjectsCreateAction, project) } catch (e: Exception) { when (e) { - is RealmUninitialisedException -> Unit // AuthStore hasn't yet saved the project, no need to do anything + is RealmUninitialisedException -> Unit + + // AuthStore hasn't yet saved the project, no need to do anything else -> Simber.e("Failed to tokenize existing records", e) } } @@ -94,6 +96,7 @@ internal class EnrolmentRecordRepositoryImpl @Inject constructor( project: Project, ) = when (value) { is TokenizableString.Tokenized -> value + is TokenizableString.Raw -> tokenizationProcessor.encrypt( decrypted = value, tokenKeyType = tokenKeyType, @@ -102,20 +105,20 @@ internal class EnrolmentRecordRepositoryImpl @Inject constructor( } override suspend fun count( - query: SubjectQuery, + query: EnrolmentRecordQuery, dataSource: BiometricDataSource, - ): Int = fromIdentityDataSource(dataSource).count(query, dataSource) + ): Int = fromCandidateDataSource(dataSource).count(query, dataSource) override suspend fun getLocalDBInfo(): String = selectEnrolmentRecordLocalDataSource().getLocalDBInfo() - override suspend fun loadIdentities( - query: SubjectQuery, + override suspend fun loadCandidateRecords( + query: EnrolmentRecordQuery, ranges: List, dataSource: BiometricDataSource, project: Project, scope: CoroutineScope, onCandidateLoaded: suspend () -> Unit, - ) = fromIdentityDataSource(dataSource).loadIdentities( + ) = fromCandidateDataSource(dataSource).loadCandidateRecords( query = query, ranges = ranges, dataSource = dataSource, @@ -124,21 +127,21 @@ internal class EnrolmentRecordRepositoryImpl @Inject constructor( onCandidateLoaded = onCandidateLoaded, ) - private suspend fun fromIdentityDataSource(dataSource: BiometricDataSource) = when (dataSource) { + private suspend fun fromCandidateDataSource(dataSource: BiometricDataSource) = when (dataSource) { is BiometricDataSource.Simprints -> selectEnrolmentRecordLocalDataSource() is BiometricDataSource.CommCare -> commCareDataSource } - override suspend fun load(query: SubjectQuery): List = selectEnrolmentRecordLocalDataSource().load(query) + override suspend fun load(query: EnrolmentRecordQuery): List = selectEnrolmentRecordLocalDataSource().load(query) override suspend fun getAllSubjectIds(): List = selectEnrolmentRecordLocalDataSource().getAllSubjectIds() - override suspend fun delete(queries: List) = selectEnrolmentRecordLocalDataSource().delete(queries) + override suspend fun delete(queries: List) = selectEnrolmentRecordLocalDataSource().delete(queries) override suspend fun deleteAll() = selectEnrolmentRecordLocalDataSource().deleteAll() override suspend fun performActions( - actions: List, + actions: List, project: Project, ) { insertRecordsInRoomDuringMigration(actions, project) diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordsStoreModule.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordsStoreModule.kt index f48094d87d..51c9628983 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordsStoreModule.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordsStoreModule.kt @@ -7,7 +7,7 @@ import com.simprints.core.tools.json.JsonHelper import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.utils.EncodingUtils import com.simprints.core.tools.utils.ExtractCommCareCaseIdUseCase -import com.simprints.infra.enrolment.records.repository.commcare.CommCareIdentityDataSource +import com.simprints.infra.enrolment.records.repository.commcare.CommCareCandidateRecordDataSource import com.simprints.infra.enrolment.records.repository.remote.EnrolmentRecordRemoteDataSource import com.simprints.infra.enrolment.records.repository.remote.EnrolmentRecordRemoteDataSourceImpl import com.simprints.infra.enrolment.records.repository.usecases.CompareImplicitTokenizedStringsUseCase @@ -48,7 +48,7 @@ class IdentityDataSourceModule { @AvailableProcessors availableProcessors: Int, @ApplicationContext context: Context, @DispatcherIO dispatcher: CoroutineDispatcher, - ): IdentityDataSource = CommCareIdentityDataSource( + ): CandidateRecordDataSource = CommCareCandidateRecordDataSource( timeHelper = timeHelper, encoder = encoder, jsonHelper = jsonHelper, diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareIdentityDataSource.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareCandidateRecordDataSource.kt similarity index 79% rename from infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareIdentityDataSource.kt rename to infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareCandidateRecordDataSource.kt index a6c0619363..dd24b875bd 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareIdentityDataSource.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareCandidateRecordDataSource.kt @@ -9,8 +9,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule import com.simprints.core.AvailableProcessors import com.simprints.core.DispatcherBG import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameDeserializer import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameSerializer @@ -20,10 +20,10 @@ import com.simprints.core.tools.utils.EncodingUtils import com.simprints.core.tools.utils.ExtractCommCareCaseIdUseCase import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.store.models.TokenKeyType -import com.simprints.infra.enrolment.records.repository.IdentityDataSource +import com.simprints.infra.enrolment.records.repository.CandidateRecordDataSource import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.usecases.CompareImplicitTokenizedStringsUseCase import com.simprints.infra.events.event.cosync.CoSyncEnrolmentRecordCreationEventDeserializer import com.simprints.infra.events.event.cosync.CoSyncEnrolmentRecordEvents @@ -44,8 +44,9 @@ import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.withContext import javax.inject.Inject +import com.simprints.core.domain.reference.BiometricReference as CoreBiometricReference -internal class CommCareIdentityDataSource @Inject constructor( +internal class CommCareCandidateRecordDataSource @Inject constructor( private val timeHelper: TimeHelper, private val encoder: EncodingUtils, private val jsonHelper: JsonHelper, @@ -54,19 +55,19 @@ internal class CommCareIdentityDataSource @Inject constructor( @AvailableProcessors private val availableProcessors: Int, @ApplicationContext private val context: Context, @DispatcherBG private val dispatcher: CoroutineDispatcher, -) : IdentityDataSource { +) : CandidateRecordDataSource { private fun getCaseMetadataUri(packageName: String): Uri = "content://$packageName.case/casedb/case".toUri() private fun getCaseDataUri(packageName: String): Uri = "content://$packageName.case/casedb/data".toUri() - override suspend fun loadIdentities( - query: SubjectQuery, + override suspend fun loadCandidateRecords( + query: EnrolmentRecordQuery, ranges: List, dataSource: BiometricDataSource, project: Project, scope: CoroutineScope, onCandidateLoaded: suspend () -> Unit, - ): ReceiveChannel = loadIdentitiesConcurrently( + ): ReceiveChannel = loadIdentitiesConcurrently( ranges = ranges, scope = scope, ) { range -> @@ -79,15 +80,15 @@ internal class CommCareIdentityDataSource @Inject constructor( onCandidateLoaded = onCandidateLoaded, ) val endTime = timeHelper.now() - IdentityBatch(identities, startTime, endTime) + CandidateRecordBatch(identities, startTime, endTime) } private fun loadIdentitiesConcurrently( ranges: List, scope: CoroutineScope, - load: suspend (IntRange) -> IdentityBatch, - ): ReceiveChannel { - val channel = Channel(availableProcessors) + load: suspend (IntRange) -> CandidateRecordBatch, + ): ReceiveChannel { + val channel = Channel(availableProcessors) val semaphore = Semaphore(availableProcessors) scope.launch(dispatcher) { ranges @@ -100,43 +101,43 @@ internal class CommCareIdentityDataSource @Inject constructor( } private suspend fun loadIdentities( - query: SubjectQuery, + query: EnrolmentRecordQuery, range: IntRange, dataSource: BiometricDataSource, project: Project, onCandidateLoaded: suspend () -> Unit, - ): List = loadEnrolmentRecordCreationEvents(range, dataSource.callerPackageName(), query, project, onCandidateLoaded) + ): List = loadEnrolmentRecordCreationEvents(range, dataSource.callerPackageName(), query, project, onCandidateLoaded) .filter { erce -> erce.payload.biometricReferences.any { it.format == query.format } } .map { erce -> - Identity( + CandidateRecord( erce.payload.subjectId, - erce.payload.biometricReferences.flatMap { reference -> - when (reference) { - is FaceReference -> reference.templates.mapNotNull { faceTemplate -> - if (reference.format != query.format) { - null - } else { - Sample( - template = encoder.base64ToBytes(faceTemplate.template), - format = reference.format, - referenceId = reference.id, - modality = Modality.FACE, - ) - } - } + erce.payload.biometricReferences.mapNotNull { reference -> + if (reference.format != query.format) { + null + } else { + when (reference) { + is FaceReference -> CoreBiometricReference( + referenceId = reference.id, + format = reference.format, + modality = Modality.FACE, + templates = reference.templates.map { + BiometricTemplate( + template = encoder.base64ToBytes(it.template), + ) + }, + ) - is FingerprintReference -> reference.templates.mapNotNull { fingerprintTemplate -> - if (reference.format != query.format) { - null - } else { - Sample( - identifier = fingerprintTemplate.finger, - template = encoder.base64ToBytes(fingerprintTemplate.template), - format = reference.format, - referenceId = reference.id, - modality = Modality.FINGERPRINT, - ) - } + is FingerprintReference -> CoreBiometricReference( + referenceId = reference.id, + format = reference.format, + modality = Modality.FINGERPRINT, + templates = reference.templates.map { + BiometricTemplate( + identifier = it.finger, + template = encoder.base64ToBytes(it.template), + ) + }, + ) } } }, @@ -146,7 +147,7 @@ internal class CommCareIdentityDataSource @Inject constructor( private suspend fun loadEnrolmentRecordCreationEvents( range: IntRange, callerPackageName: String, - query: SubjectQuery, + query: EnrolmentRecordQuery, project: Project, onCandidateLoaded: suspend () -> Unit, ): List { @@ -186,7 +187,7 @@ internal class CommCareIdentityDataSource @Inject constructor( private fun loadEnrolmentRecordCreationEvents( caseId: String, callerPackageName: String, - query: SubjectQuery, + query: EnrolmentRecordQuery, project: Project, ): List { // Access Case Data Listing for the caseId @@ -214,12 +215,12 @@ internal class CommCareIdentityDataSource @Inject constructor( } private fun isSubjectIdNullOrMatching( - query: SubjectQuery, + query: EnrolmentRecordQuery, event: EnrolmentRecordCreationEvent, ): Boolean = query.subjectId == null || query.subjectId == event.payload.subjectId private fun isAttendantIdNullOrMatching( - query: SubjectQuery, + query: EnrolmentRecordQuery, event: EnrolmentRecordCreationEvent, project: Project, ): Boolean = query.attendantId == null || @@ -231,7 +232,7 @@ internal class CommCareIdentityDataSource @Inject constructor( ) private fun isModuleIdNullOrMatching( - query: SubjectQuery, + query: EnrolmentRecordQuery, event: EnrolmentRecordCreationEvent, project: Project, ): Boolean = query.moduleId == null || @@ -281,7 +282,7 @@ internal class CommCareIdentityDataSource @Inject constructor( } override suspend fun count( - query: SubjectQuery, + query: EnrolmentRecordQuery, dataSource: BiometricDataSource, ): Int = withContext(dispatcher) { var count = 0 diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/IdentityBatch.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/CandidateRecordBatch.kt similarity index 58% rename from infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/IdentityBatch.kt rename to infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/CandidateRecordBatch.kt index e52f5f6978..7bb7b2804b 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/IdentityBatch.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/CandidateRecordBatch.kt @@ -1,10 +1,10 @@ package com.simprints.infra.enrolment.records.repository.domain.models -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.tools.time.Timestamp -data class IdentityBatch( - val identities: List, +data class CandidateRecordBatch( + val identities: List, val loadingStartTime: Timestamp, val loadingEndTime: Timestamp, ) diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/Subject.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecord.kt similarity index 76% rename from infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/Subject.kt rename to infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecord.kt index 1e6f50cac6..0e5c3dc63d 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/Subject.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecord.kt @@ -2,19 +2,19 @@ package com.simprints.infra.enrolment.records.repository.domain.models import android.os.Parcelable import com.simprints.core.domain.externalcredential.ExternalCredential -import com.simprints.core.domain.sample.Sample import com.simprints.core.domain.tokenization.TokenizableString import kotlinx.parcelize.Parcelize import java.util.Date +import com.simprints.core.domain.reference.BiometricReference as CoreBiometricReference @Parcelize -data class Subject( +data class EnrolmentRecord( val subjectId: String, val projectId: String, val attendantId: TokenizableString, val moduleId: TokenizableString, val createdAt: Date? = null, val updatedAt: Date? = null, - var samples: List = emptyList(), + var references: List = emptyList(), var externalCredentials: List = emptyList(), ) : Parcelable diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectAction.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecordAction.kt similarity index 62% rename from infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectAction.kt rename to infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecordAction.kt index b9ce0ea0e6..945abe4e3b 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectAction.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecordAction.kt @@ -2,23 +2,23 @@ package com.simprints.infra.enrolment.records.repository.domain.models import androidx.annotation.Keep import com.simprints.core.domain.externalcredential.ExternalCredential -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference @Keep -sealed class SubjectAction { +sealed class EnrolmentRecordAction { data class Creation( - val subject: Subject, - ) : SubjectAction() + val enrolmentRecord: EnrolmentRecord, + ) : EnrolmentRecordAction() data class Update( val subjectId: String, - val samplesToAdd: List, + val samplesToAdd: List, val referenceIdsToRemove: List, val externalCredentialsToAdd: List, val externalCredentialIdsToRemove: List, - ) : SubjectAction() + ) : EnrolmentRecordAction() data class Deletion( val subjectId: String, - ) : SubjectAction() + ) : EnrolmentRecordAction() } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectQuery.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecordQuery.kt similarity index 95% rename from infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectQuery.kt rename to infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecordQuery.kt index 3a2e5f29b0..755f1a9cec 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectQuery.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/EnrolmentRecordQuery.kt @@ -5,7 +5,7 @@ import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.tokenization.TokenizableString @Keep -data class SubjectQuery( +data class EnrolmentRecordQuery( val projectId: String? = null, val subjectId: String? = null, val subjectIds: List? = null, diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/EnrolmentRecordLocalDataSource.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/EnrolmentRecordLocalDataSource.kt index 76742abcd1..6233f38a1e 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/EnrolmentRecordLocalDataSource.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/EnrolmentRecordLocalDataSource.kt @@ -1,20 +1,20 @@ package com.simprints.infra.enrolment.records.repository.local import com.simprints.infra.config.store.models.Project -import com.simprints.infra.enrolment.records.repository.IdentityDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.CandidateRecordDataSource +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery -interface EnrolmentRecordLocalDataSource : IdentityDataSource { - suspend fun load(query: SubjectQuery): List +interface EnrolmentRecordLocalDataSource : CandidateRecordDataSource { + suspend fun load(query: EnrolmentRecordQuery): List - suspend fun delete(queries: List) + suspend fun delete(queries: List) suspend fun deleteAll() suspend fun performActions( - actions: List, + actions: List, project: Project, ) diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSource.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSource.kt index db45ff1d43..27aed59da3 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSource.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSource.kt @@ -2,7 +2,7 @@ package com.simprints.infra.enrolment.records.repository.local import com.simprints.core.DispatcherIO import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.tools.time.TimeHelper import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.store.models.TokenKeyType @@ -10,10 +10,10 @@ import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.enrolment.records.realm.store.RealmWrapper import com.simprints.infra.enrolment.records.realm.store.models.DbSubject import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.local.models.toDomain import com.simprints.infra.enrolment.records.repository.local.models.toRealmDb import com.simprints.infra.enrolment.records.repository.local.models.toRealmFaceDb @@ -61,7 +61,7 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( const val CHANNEL_CAPACITY = 4 } - override suspend fun load(query: SubjectQuery): List = realmWrapper.readRealm { + override suspend fun load(query: EnrolmentRecordQuery): List = realmWrapper.readRealm { it .query(DbSubject::class) .buildRealmQueryForSubject(query) @@ -69,46 +69,46 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( .map { dbSubject -> dbSubject.toDomain() } } - override suspend fun loadIdentities( - query: SubjectQuery, + override suspend fun loadCandidateRecords( + query: EnrolmentRecordQuery, ranges: List, dataSource: BiometricDataSource, project: Project, scope: CoroutineScope, onCandidateLoaded: suspend () -> Unit, - ): ReceiveChannel { - val channel = Channel(CHANNEL_CAPACITY) + ): ReceiveChannel { + val channel = Channel(CHANNEL_CAPACITY) scope.launch(dispatcherIO) { ranges.forEach { range -> val startTime = timeHelper.now() - val identities = loadIdentitiesRange( + val candidates = loadCandidatesRange( query = query, range = range, mapper = { dbSubject -> - Identity( + CandidateRecord( subjectId = dbSubject.subjectId.toString(), - samples = ( - dbSubject.faceSamples.map { it.toDomain() } + - dbSubject.fingerprintSamples.map { it.toDomain() } + references = ( + dbSubject.faceSamples.toDomain() + + dbSubject.fingerprintSamples.toDomain() ).filter { it.format == query.format }, ) }, onCandidateLoaded = onCandidateLoaded, ) val endTime = timeHelper.now() - channel.send(IdentityBatch(identities, startTime, endTime)) + channel.send(CandidateRecordBatch(candidates, startTime, endTime)) } channel.close() } return channel } - private suspend fun loadIdentitiesRange( - query: SubjectQuery, + private suspend fun loadCandidatesRange( + query: EnrolmentRecordQuery, range: IntRange, - mapper: (DbSubject) -> Identity, + mapper: (DbSubject) -> CandidateRecord, onCandidateLoaded: suspend () -> Unit, - ): List = realmWrapper.readRealm { realm -> + ): List = realmWrapper.readRealm { realm -> realm .query(DbSubject::class) .buildRealmQueryForSubject(query) @@ -121,7 +121,7 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( } } - override suspend fun delete(queries: List) { + override suspend fun delete(queries: List) { realmWrapper.writeRealm { realm -> queries.forEach { realm.delete(realm.query(DbSubject::class).buildRealmQueryForSubject(it)) @@ -136,7 +136,7 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( } override suspend fun count( - query: SubjectQuery, + query: EnrolmentRecordQuery, dataSource: BiometricDataSource, ): Int = realmWrapper.readRealm { realm -> realm @@ -148,7 +148,7 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( } override suspend fun performActions( - actions: List, + actions: List, project: Project, ) { // if there is no actions to perform return to avoid useless realm operations @@ -160,17 +160,17 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( realmWrapper.writeRealm { realm -> actions.forEach { action -> when (action) { - is SubjectAction.Creation -> { - val newSubject = action.subject + is EnrolmentRecordAction.Creation -> { + val newSubject = action.enrolmentRecord .copy( moduleId = tokenizationProcessor.tokenizeIfNecessary( - action.subject.moduleId, + action.enrolmentRecord.moduleId, TokenKeyType.ModuleId, project, ), attendantId = tokenizationProcessor.tokenizeIfNecessary( - action.subject.attendantId, + action.enrolmentRecord.attendantId, TokenKeyType.AttendantId, project, ), @@ -201,7 +201,7 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( realm.copyToRealm(newSubject, updatePolicy = UpdatePolicy.ALL) } - is SubjectAction.Update -> { + is EnrolmentRecordAction.Update -> { val dbSubject: DbSubject? = realm.findSubject(RealmUUID.from(action.subjectId)) if (dbSubject != null) { val referencesToDelete = action.referenceIdsToRemove.toSet() // to make lookup O(1) @@ -218,12 +218,12 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( dbSubject.faceSamples = ( faceSamplesMap[false].orEmpty() + action.samplesToAdd .filter { it.modality == Modality.FACE } - .map { it.toRealmFaceDb() } + .flatMap { it.toRealmFaceDb() } ).toRealmList() dbSubject.fingerprintSamples = ( fingerprintSamplesMap[false].orEmpty() + action.samplesToAdd .filter { it.modality == Modality.FINGERPRINT } - .map { it.toRealmFingerprintDb() } + .flatMap { it.toRealmFingerprintDb() } ).toRealmList() dbSubject.externalCredentials = allExternalCredentials.toRealmList() @@ -236,12 +236,14 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( } } - is SubjectAction.Deletion -> realm.delete( - realm - .query(DbSubject::class) - .buildRealmQueryForSubject(query = SubjectQuery(subjectId = action.subjectId)) - .find(), - ) + is EnrolmentRecordAction.Deletion -> { + realm.delete( + realm + .query(DbSubject::class) + .buildRealmQueryForSubject(query = EnrolmentRecordQuery(subjectId = action.subjectId)) + .find(), + ) + } } } } @@ -267,7 +269,7 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( private fun MutableRealm.findSubject(subjectId: RealmUUID): DbSubject? = query(DbSubject::class).query("$SUBJECT_ID_FIELD == $0", subjectId).first().find() - private fun RealmQuery.buildRealmQueryForSubject(query: SubjectQuery): RealmQuery { + private fun RealmQuery.buildRealmQueryForSubject(query: EnrolmentRecordQuery): RealmQuery { var realmQuery = this if (query.projectId != null) { @@ -326,7 +328,7 @@ internal class RealmEnrolmentRecordLocalDataSource @Inject constructor( /** * Loads all subjects in batches of the specified size. */ - fun loadAllSubjectsInBatches(batchSize: Int): Flow> = channelFlow { + fun loadAllSubjectsInBatches(batchSize: Int): Flow> = channelFlow { require(batchSize > 0) { "Batch size must be greater than 0" } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSource.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSource.kt index e9af15d33c..34371f4eaf 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSource.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSource.kt @@ -2,27 +2,25 @@ package com.simprints.infra.enrolment.records.repository.local import androidx.room.withTransaction import com.simprints.core.DispatcherIO -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.tools.time.TimeHelper import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery -import com.simprints.infra.enrolment.records.repository.local.models.DbSampleIdentifier +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery +import com.simprints.infra.enrolment.records.repository.local.models.toBiometricReferences import com.simprints.infra.enrolment.records.repository.local.models.toDomain -import com.simprints.infra.enrolment.records.repository.local.models.toRoomDb +import com.simprints.infra.enrolment.records.repository.local.models.toRoomDbCredentials +import com.simprints.infra.enrolment.records.repository.local.models.toRoomDbTemplate import com.simprints.infra.enrolment.records.room.store.BuildConfig.DB_ENCRYPTION import com.simprints.infra.enrolment.records.room.store.SubjectDao import com.simprints.infra.enrolment.records.room.store.SubjectsDatabase import com.simprints.infra.enrolment.records.room.store.SubjectsDatabaseFactory import com.simprints.infra.enrolment.records.room.store.models.DbBiometricTemplate -import com.simprints.infra.enrolment.records.room.store.models.DbModality import com.simprints.infra.enrolment.records.room.store.models.DbSubject -import com.simprints.infra.enrolment.records.room.store.models.toDomain import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ROOM_RECORDS_DB import com.simprints.infra.logging.Simber import kotlinx.coroutines.CoroutineDispatcher @@ -34,7 +32,7 @@ import kotlinx.coroutines.withContext import java.io.File import javax.inject.Inject import javax.inject.Singleton -import com.simprints.infra.enrolment.records.repository.domain.models.Subject as DomainSubject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord as DomainSubject /** * Local data source for enrolment records using Room. @@ -60,7 +58,7 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( * Don't use this method if the query contains format fields (faceSampleFormat or fingerprintSampleFormat). * instead, use loadFaceIdentities or loadFingerprintIdentities methods. */ - override suspend fun load(query: SubjectQuery): List = withContext(dispatcherIO) { + override suspend fun load(query: EnrolmentRecordQuery): List = withContext(dispatcherIO) { if (query.hasUntokenizedFields == true) { Simber.d( "[load] Query has untokenized fields, returning empty list as all records are tokenized.", @@ -75,7 +73,7 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( * Counts subjects matching the given query. */ override suspend fun count( - query: SubjectQuery, + query: EnrolmentRecordQuery, dataSource: BiometricDataSource, ): Int = withContext(dispatcherIO) { subjectDao.countSubjects(queryBuilder.buildCountQuery(query)) @@ -84,30 +82,21 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( /** * Loads identities in paged ranges. */ - override suspend fun loadIdentities( - query: SubjectQuery, + override suspend fun loadCandidateRecords( + query: EnrolmentRecordQuery, ranges: List, dataSource: BiometricDataSource, project: Project, scope: CoroutineScope, onCandidateLoaded: suspend () -> Unit, - ): ReceiveChannel = loadBiometricIdentitiesPaged( + ): ReceiveChannel = loadBiometricIdentitiesPaged( query = query, ranges = ranges, format = requireNotNull(query.format) { "format required" }, - createIdentity = { subjectId, templates -> - Identity( + createCandidateRecord = { subjectId, templates -> + CandidateRecord( subjectId = subjectId, - samples = templates.map { sample -> - Sample( - id = sample.uuid, - template = sample.templateData, - identifier = DbSampleIdentifier.fromId(sample.identifier).toDomain(), - format = sample.format, - referenceId = sample.referenceId, - modality = DbModality.fromId(sample.modality).toDomain(), - ) - }, + references = templates.toBiometricReferences(), ) }, onCandidateLoaded = onCandidateLoaded, @@ -115,16 +104,16 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( ) private fun loadBiometricIdentitiesPaged( - query: SubjectQuery, + query: EnrolmentRecordQuery, ranges: List, format: String, - createIdentity: (String, List) -> Identity, + createCandidateRecord: (String, List) -> CandidateRecord, onCandidateLoaded: suspend () -> Unit, scope: CoroutineScope, - ): ReceiveChannel { + ): ReceiveChannel { var afterSubjectId: String? = null var lastOffset = 0 - val channel = Channel(CHANNEL_CAPACITY) + val channel = Channel(CHANNEL_CAPACITY) scope.launch(dispatcherIO) { ranges .forEach { range -> @@ -137,13 +126,13 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( query = query.copy(afterSubjectId = afterSubjectId), // update query with the last seen subject ID pageSize = range.last - range.first + 1, format = format, - createIdentity = createIdentity, + createCandidateRecord = createCandidateRecord, onCandidateLoaded = onCandidateLoaded, ) afterSubjectId = identities.lastOrNull()?.subjectId lastOffset = range.last + 1 val endTime = timeHelper.now() - channel.send(IdentityBatch(identities, startTime, endTime)) + channel.send(CandidateRecordBatch(identities, startTime, endTime)) } channel.close() } @@ -151,22 +140,22 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( } private suspend fun loadBiometricIdentities( - query: SubjectQuery, + query: EnrolmentRecordQuery, pageSize: Int, format: String?, - createIdentity: (subjectId: String, samples: List) -> Identity, + createCandidateRecord: (subjectId: String, samples: List) -> CandidateRecord, onCandidateLoaded: suspend () -> Unit, - ): List = withContext(dispatcherIO) { + ): List = withContext(dispatcherIO) { requireNotNull(format) { "Appropriate sampleFormat is required for loading biometric identities." } subjectDao .loadSamples(queryBuilder.buildBiometricTemplatesQuery(query, pageSize)) .map { (subjectId, templates) -> onCandidateLoaded() - createIdentity(subjectId, templates) + createCandidateRecord(subjectId, templates) } } - override suspend fun delete(queries: List): Unit = withContext(dispatcherIO) { + override suspend fun delete(queries: List): Unit = withContext(dispatcherIO) { Simber.i("[delete] Deleting subjects with queries: $queries", tag = ROOM_RECORDS_DB) database.withTransaction { queries.forEach { query -> @@ -177,19 +166,19 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( override suspend fun deleteAll(): Unit = withContext(dispatcherIO) { Simber.i("[deleteAll] Deleting all subjects.", tag = ROOM_RECORDS_DB) - subjectDao.deleteSubjects(queryBuilder.buildDeleteQuery(SubjectQuery())) + subjectDao.deleteSubjects(queryBuilder.buildDeleteQuery(EnrolmentRecordQuery())) } override suspend fun performActions( - actions: List, + actions: List, project: Project, ) { database.withTransaction { actions.forEach { action -> when (action) { - is SubjectAction.Creation -> createSubject(action.subject, project) - is SubjectAction.Update -> updateSubject(action) - is SubjectAction.Deletion -> deleteSubject(action.subjectId) + is EnrolmentRecordAction.Creation -> createSubject(action.enrolmentRecord, project) + is EnrolmentRecordAction.Update -> updateSubject(action) + is EnrolmentRecordAction.Deletion -> deleteSubject(action.subjectId) } } } @@ -202,7 +191,7 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( val dbPath = database.openHelper.readableDatabase.path val dbSize = getTotalRoomDbSizeBytes(dbPath!!) val isDBEncrypted = DB_ENCRYPTION - val subjectCount = subjectDao.countSubjects(queryBuilder.buildCountQuery(SubjectQuery())) + val subjectCount = subjectDao.countSubjects(queryBuilder.buildCountQuery(EnrolmentRecordQuery())) "Room DB Info:\n" + "Database Name: ${database.openHelper.databaseName}\n" + "Database Version: $dbVersion\n" + @@ -227,7 +216,7 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( subject: DomainSubject, project: Project, ) { - require(subject.samples.isNotEmpty()) { + require(subject.references.isNotEmpty()) { val errorMsg = "Subject should include at least one sample" Simber.i( "[createSubject] $errorMsg for subjectId: ${subject.subjectId}", @@ -254,17 +243,17 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( updatedAt = subject.updatedAt?.time, ) subjectDao.insertSubject(dbSubject) - subject.samples.takeIf { it.isNotEmpty() }?.let { samples -> - val dbSample = samples.map { it.toRoomDb(subject.subjectId) } - subjectDao.insertBiometricSamples(dbSample) + subject.references.takeIf { it.isNotEmpty() }?.map { reference -> + val dbBiometricTemplates = reference.toRoomDbTemplate(subject.subjectId) + subjectDao.insertBiometricSamples(dbBiometricTemplates) } subject.externalCredentials.takeIf { it.isNotEmpty() }?.let { credentials -> - val dbExternalCredentials = credentials.map { it.toRoomDb() } + val dbExternalCredentials = credentials.map { it.toRoomDbCredentials() } subjectDao.insertExternalCredentials(dbExternalCredentials) } } - private suspend fun updateSubject(action: SubjectAction.Update) { + private suspend fun updateSubject(action: EnrolmentRecordAction.Update) { val dbSubject = subjectDao.getSubject(action.subjectId) if (dbSubject != null) { val referencesToDelete = action.referenceIdsToRemove.toSet() @@ -281,7 +270,7 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( dbSubject.biometricTemplates.filter { it.referenceId in referencesToDelete }.forEach { subjectDao.deleteBiometricSample(it.uuid) } - val templatesToAdd = action.samplesToAdd.map { it.toRoomDb(action.subjectId) } + val templatesToAdd = action.samplesToAdd.flatMap { it.toRoomDbTemplate(action.subjectId) } if (templatesToAdd.isNotEmpty()) { subjectDao.insertBiometricSamples(templatesToAdd) } @@ -289,7 +278,7 @@ internal class RoomEnrolmentRecordLocalDataSource @Inject constructor( subjectDao.deleteExternalCredentialById(it.id) } if (action.externalCredentialsToAdd.isNotEmpty()) { - subjectDao.insertExternalCredentials(action.externalCredentialsToAdd.map { it.toRoomDb() }) + subjectDao.insertExternalCredentials(action.externalCredentialsToAdd.map { it.toRoomDbCredentials() }) } } else { Simber.e( diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilder.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilder.kt index 7e87f3b570..3d50288147 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilder.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilder.kt @@ -1,7 +1,7 @@ package com.simprints.infra.enrolment.records.repository.local import androidx.sqlite.db.SimpleSQLiteQuery -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.room.store.models.DbBiometricTemplate.Companion.FORMAT_COLUMN import com.simprints.infra.enrolment.records.room.store.models.DbBiometricTemplate.Companion.TEMPLATE_TABLE_NAME import com.simprints.infra.enrolment.records.room.store.models.DbExternalCredential.Companion.EXTERNAL_CREDENTIAL_TABLE_NAME @@ -17,13 +17,13 @@ import javax.inject.Inject internal class RoomEnrolmentRecordQueryBuilder @Inject constructor() { /** - * Builds a query to select subjects based on the provided [SubjectQuery]. + * Builds a query to select subjects based on the provided [EnrolmentRecordQuery]. * The query will be on the `SUBJECT_TABLE_NAME` table and will include filtering criteria - * Don't set the format in the [SubjectQuery] for this method instead use [buildBiometricTemplatesQuery]. - * @param query The [SubjectQuery] containing the filtering criteria. + * Don't set the format in the [EnrolmentRecordQuery] for this method instead use [buildBiometricTemplatesQuery]. + * @param query The [EnrolmentRecordQuery] containing the filtering criteria. * @return A [SimpleSQLiteQuery] that can be executed against the database. */ - fun buildSubjectQuery(query: SubjectQuery): SimpleSQLiteQuery { + fun buildSubjectQuery(query: EnrolmentRecordQuery): SimpleSQLiteQuery { // require format not to be set for subject query and guide to use the buildBiometricTemplatesQuery instead require(query.format == null) { "Cannot set format for subject query, use buildBiometricTemplatesQuery instead" @@ -41,7 +41,7 @@ internal class RoomEnrolmentRecordQueryBuilder @Inject constructor() { return SimpleSQLiteQuery(sql, args.toTypedArray()) } - fun buildCountQuery(query: SubjectQuery): SimpleSQLiteQuery { + fun buildCountQuery(query: EnrolmentRecordQuery): SimpleSQLiteQuery { val (whereClause, args) = buildWhereClause(query) val sql = if (query.format != null) { @@ -54,7 +54,7 @@ internal class RoomEnrolmentRecordQueryBuilder @Inject constructor() { } fun buildBiometricTemplatesQuery( - query: SubjectQuery, + query: EnrolmentRecordQuery, pageSize: Int, ): SimpleSQLiteQuery { // require format to be set for biometric templates query @@ -80,7 +80,7 @@ internal class RoomEnrolmentRecordQueryBuilder @Inject constructor() { return SimpleSQLiteQuery(sql, args.toTypedArray()) } - fun buildDeleteQuery(query: SubjectQuery): SimpleSQLiteQuery { + fun buildDeleteQuery(query: EnrolmentRecordQuery): SimpleSQLiteQuery { require(query.format == null) { val errorMsg = "format is not supported for deletion" Simber.i("[delete] $errorMsg", tag = ROOM_RECORDS_DB) @@ -97,7 +97,7 @@ internal class RoomEnrolmentRecordQueryBuilder @Inject constructor() { } private fun buildWhereClause( - query: SubjectQuery, + query: EnrolmentRecordQuery, subjectAlias: String = "S.", // Default alias for subject table, dot included. Empty string for no alias. templateAlias: String = "T.", // Default alias for template table, dot included. Empty string for no alias. credentialAlias: String = "C.", // Default alias for credentials table, dot included. Empty string for no alias. @@ -150,7 +150,7 @@ internal class RoomEnrolmentRecordQueryBuilder @Inject constructor() { } private fun buildCredentialJoinClause( - query: SubjectQuery, + query: EnrolmentRecordQuery, subjectAlias: String = "S", credentialAlias: String = "C", ): String = if (query.externalCredential != null) { @@ -160,7 +160,7 @@ internal class RoomEnrolmentRecordQueryBuilder @Inject constructor() { } private fun buildOrderByClause( - query: SubjectQuery, + query: EnrolmentRecordQuery, subjectAlias: String = "S.", ) = if (query.sort) { "ORDER BY $subjectAlias$SUBJECT_ID_COLUMN ASC" diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCase.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCase.kt index e5f39cb792..08fe5e517d 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCase.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCase.kt @@ -1,7 +1,7 @@ package com.simprints.infra.enrolment.records.repository.local.migration import com.simprints.infra.config.store.models.Project -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.enrolment.records.repository.local.RoomEnrolmentRecordLocalDataSource import com.simprints.infra.logging.LoggingConstants.CrashReportTag.REALM_DB_MIGRATION import com.simprints.infra.logging.Simber @@ -13,13 +13,13 @@ internal class InsertRecordsInRoomDuringMigrationUseCase @Inject constructor( private val roomEnrolmentRecordLocalDataSource: RoomEnrolmentRecordLocalDataSource, ) { suspend operator fun invoke( - actions: List, + actions: List, project: Project, ) { // if the realm to room migration is in progress, we need to insert the records in the new db too if (realmToRoomMigrationFlagsStore.isMigrationInProgress()) { - actions.filterIsInstance().forEach { + actions.filterIsInstance().forEach { insertRecordInRoom(it, project) } logUpdatesDuringMigration(actions) @@ -27,18 +27,18 @@ internal class InsertRecordsInRoomDuringMigrationUseCase @Inject constructor( } private suspend fun insertRecordInRoom( - creation: SubjectAction.Creation, + creation: EnrolmentRecordAction.Creation, project: Project, ) { roomEnrolmentRecordLocalDataSource.performActions(actions = listOf(creation), project) Simber.i( - "[InsertRecordsInRoomDuringMigrationUseCase] Inserted subject ${creation.subject.subjectId} enrolled during migration", + "[InsertRecordsInRoomDuringMigrationUseCase] Inserted subject ${creation.enrolmentRecord.subjectId} enrolled during migration", tag = REALM_DB_MIGRATION, ) } - private fun logUpdatesDuringMigration(actions: List) { - if (actions.any { it is SubjectAction.Update || it is SubjectAction.Deletion }) { + private fun logUpdatesDuringMigration(actions: List) { + if (actions.any { it is EnrolmentRecordAction.Update || it is EnrolmentRecordAction.Deletion }) { // if actions contains any updates or deletes and the migration is in progress, log them as an error Simber.e( "[EnrolmentRecordRepositoryImpl] Actions during migration: ${actions.joinToString(", ")}", diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorker.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorker.kt index 46f0b8bbb9..bdb0e31916 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorker.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorker.kt @@ -6,8 +6,8 @@ import androidx.work.WorkerParameters import com.simprints.core.DispatcherIO import com.simprints.core.workers.SimCoroutineWorker import com.simprints.infra.config.store.ConfigRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.local.RealmEnrolmentRecordLocalDataSource import com.simprints.infra.enrolment.records.repository.local.RoomEnrolmentRecordLocalDataSource import com.simprints.infra.logging.LoggingConstants.CrashReportTag.REALM_DB_MIGRATION @@ -50,7 +50,7 @@ internal class RealmToRoomMigrationWorker @AssistedInject constructor( realmToRoomMigrationFlagsStore.updateStatus(MigrationStatus.IN_PROGRESS) // 2. Check if realm data source is empty - val recordsCount = realmDataSource.count(SubjectQuery()) + val recordsCount = realmDataSource.count(EnrolmentRecordQuery()) if (recordsCount > 0) { // 3. empty the room database roomDataSource.deleteAll() @@ -91,7 +91,7 @@ internal class RealmToRoomMigrationWorker @AssistedInject constructor( crashlyticsLog("[RealmToRoomMigrationWorker] Processing batch ${++index} ...") try { val subjectCreationActions = realmSubjectsBatch.map { - SubjectAction.Creation(it) + EnrolmentRecordAction.Creation(it) } roomDataSource.performActions(subjectCreationActions, project) } catch (e: Exception) { @@ -105,8 +105,8 @@ internal class RealmToRoomMigrationWorker @AssistedInject constructor( } private suspend fun validateRealmToRoomMigration() { - val realmCount = realmDataSource.count(SubjectQuery()) - val roomCount = roomDataSource.count(SubjectQuery()) + val realmCount = realmDataSource.count(EnrolmentRecordQuery()) + val roomCount = roomDataSource.count(EnrolmentRecordQuery()) if (realmCount != roomCount) { throw IllegalStateException( "Migration validation failed: Realm count ($realmCount) does not match Room count ($roomCount).", diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/DbSampleIdentifier.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/DbSampleIdentifier.kt deleted file mode 100644 index b0a4568542..0000000000 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/DbSampleIdentifier.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.simprints.infra.enrolment.records.repository.local.models - -import com.simprints.core.domain.sample.SampleIdentifier - -enum class DbSampleIdentifier( - val id: Int, -) { - RIGHT_5TH_FINGER(0), - RIGHT_4TH_FINGER(1), - RIGHT_3RD_FINGER(2), - RIGHT_INDEX_FINGER(3), - RIGHT_THUMB(4), - LEFT_THUMB(5), - LEFT_INDEX_FINGER(6), - LEFT_3RD_FINGER(7), - LEFT_4TH_FINGER(8), - LEFT_5TH_FINGER(9), - ; - - companion object Companion { - fun fromId(id: Int?) = DbSampleIdentifier.entries.firstOrNull { it.id == id } - } -} - -internal fun DbSampleIdentifier?.toDomain() = when (this) { - null -> SampleIdentifier.NONE - DbSampleIdentifier.RIGHT_5TH_FINGER -> SampleIdentifier.RIGHT_5TH_FINGER - DbSampleIdentifier.RIGHT_4TH_FINGER -> SampleIdentifier.RIGHT_4TH_FINGER - DbSampleIdentifier.RIGHT_3RD_FINGER -> SampleIdentifier.RIGHT_3RD_FINGER - DbSampleIdentifier.RIGHT_INDEX_FINGER -> SampleIdentifier.RIGHT_INDEX_FINGER - DbSampleIdentifier.RIGHT_THUMB -> SampleIdentifier.RIGHT_THUMB - DbSampleIdentifier.LEFT_THUMB -> SampleIdentifier.LEFT_THUMB - DbSampleIdentifier.LEFT_INDEX_FINGER -> SampleIdentifier.LEFT_INDEX_FINGER - DbSampleIdentifier.LEFT_3RD_FINGER -> SampleIdentifier.LEFT_3RD_FINGER - DbSampleIdentifier.LEFT_4TH_FINGER -> SampleIdentifier.LEFT_4TH_FINGER - DbSampleIdentifier.LEFT_5TH_FINGER -> SampleIdentifier.LEFT_5TH_FINGER -} - -internal fun SampleIdentifier.fromDomain() = when (this) { - SampleIdentifier.NONE -> null - SampleIdentifier.RIGHT_5TH_FINGER -> DbSampleIdentifier.RIGHT_5TH_FINGER - SampleIdentifier.RIGHT_4TH_FINGER -> DbSampleIdentifier.RIGHT_4TH_FINGER - SampleIdentifier.RIGHT_3RD_FINGER -> DbSampleIdentifier.RIGHT_3RD_FINGER - SampleIdentifier.RIGHT_INDEX_FINGER -> DbSampleIdentifier.RIGHT_INDEX_FINGER - SampleIdentifier.RIGHT_THUMB -> DbSampleIdentifier.RIGHT_THUMB - SampleIdentifier.LEFT_THUMB -> DbSampleIdentifier.LEFT_THUMB - SampleIdentifier.LEFT_INDEX_FINGER -> DbSampleIdentifier.LEFT_INDEX_FINGER - SampleIdentifier.LEFT_3RD_FINGER -> DbSampleIdentifier.LEFT_3RD_FINGER - SampleIdentifier.LEFT_4TH_FINGER -> DbSampleIdentifier.LEFT_4TH_FINGER - SampleIdentifier.LEFT_5TH_FINGER -> DbSampleIdentifier.LEFT_5TH_FINGER -} diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/DbTemplateIdentifier.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/DbTemplateIdentifier.kt new file mode 100644 index 0000000000..d75771a934 --- /dev/null +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/DbTemplateIdentifier.kt @@ -0,0 +1,51 @@ +package com.simprints.infra.enrolment.records.repository.local.models + +import com.simprints.core.domain.common.TemplateIdentifier + +enum class DbTemplateIdentifier( + val id: Int, +) { + RIGHT_5TH_FINGER(0), + RIGHT_4TH_FINGER(1), + RIGHT_3RD_FINGER(2), + RIGHT_INDEX_FINGER(3), + RIGHT_THUMB(4), + LEFT_THUMB(5), + LEFT_INDEX_FINGER(6), + LEFT_3RD_FINGER(7), + LEFT_4TH_FINGER(8), + LEFT_5TH_FINGER(9), + ; + + companion object Companion { + fun fromId(id: Int?) = DbTemplateIdentifier.entries.firstOrNull { it.id == id } + } +} + +internal fun DbTemplateIdentifier?.toDomain() = when (this) { + null -> TemplateIdentifier.NONE + DbTemplateIdentifier.RIGHT_5TH_FINGER -> TemplateIdentifier.RIGHT_5TH_FINGER + DbTemplateIdentifier.RIGHT_4TH_FINGER -> TemplateIdentifier.RIGHT_4TH_FINGER + DbTemplateIdentifier.RIGHT_3RD_FINGER -> TemplateIdentifier.RIGHT_3RD_FINGER + DbTemplateIdentifier.RIGHT_INDEX_FINGER -> TemplateIdentifier.RIGHT_INDEX_FINGER + DbTemplateIdentifier.RIGHT_THUMB -> TemplateIdentifier.RIGHT_THUMB + DbTemplateIdentifier.LEFT_THUMB -> TemplateIdentifier.LEFT_THUMB + DbTemplateIdentifier.LEFT_INDEX_FINGER -> TemplateIdentifier.LEFT_INDEX_FINGER + DbTemplateIdentifier.LEFT_3RD_FINGER -> TemplateIdentifier.LEFT_3RD_FINGER + DbTemplateIdentifier.LEFT_4TH_FINGER -> TemplateIdentifier.LEFT_4TH_FINGER + DbTemplateIdentifier.LEFT_5TH_FINGER -> TemplateIdentifier.LEFT_5TH_FINGER +} + +internal fun TemplateIdentifier.fromDomain() = when (this) { + TemplateIdentifier.NONE -> null + TemplateIdentifier.RIGHT_5TH_FINGER -> DbTemplateIdentifier.RIGHT_5TH_FINGER + TemplateIdentifier.RIGHT_4TH_FINGER -> DbTemplateIdentifier.RIGHT_4TH_FINGER + TemplateIdentifier.RIGHT_3RD_FINGER -> DbTemplateIdentifier.RIGHT_3RD_FINGER + TemplateIdentifier.RIGHT_INDEX_FINGER -> DbTemplateIdentifier.RIGHT_INDEX_FINGER + TemplateIdentifier.RIGHT_THUMB -> DbTemplateIdentifier.RIGHT_THUMB + TemplateIdentifier.LEFT_THUMB -> DbTemplateIdentifier.LEFT_THUMB + TemplateIdentifier.LEFT_INDEX_FINGER -> DbTemplateIdentifier.LEFT_INDEX_FINGER + TemplateIdentifier.LEFT_3RD_FINGER -> DbTemplateIdentifier.LEFT_3RD_FINGER + TemplateIdentifier.LEFT_4TH_FINGER -> DbTemplateIdentifier.LEFT_4TH_FINGER + TemplateIdentifier.LEFT_5TH_FINGER -> DbTemplateIdentifier.LEFT_5TH_FINGER +} diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFaceSampleConverter.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFaceSampleConverter.kt index 0120134b66..0fb8d76ca8 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFaceSampleConverter.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFaceSampleConverter.kt @@ -1,20 +1,30 @@ package com.simprints.infra.enrolment.records.repository.local.models import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.infra.enrolment.records.realm.store.models.DbFaceSample as RealmFaceSample -internal fun RealmFaceSample.toDomain(): Sample = Sample( - id = id, - template = template, - format = format, - referenceId = referenceId, - modality = Modality.FACE, -) +internal fun List.toDomain(): List = groupBy { it.referenceId } + .map { (referenceId, templates) -> + BiometricReference( + referenceId = referenceId, + format = templates.first().format, + modality = Modality.FACE, + templates = templates.map { + BiometricTemplate( + id = it.id, + template = it.template, + ) + }, + ) + } -internal fun Sample.toRealmFaceDb(): RealmFaceSample = RealmFaceSample().also { sample -> - sample.id = id - sample.referenceId = referenceId - sample.template = template - sample.format = format +internal fun BiometricReference.toRealmFaceDb(): List = templates.map { + RealmFaceSample().also { sample -> + sample.id = it.id + sample.referenceId = referenceId + sample.template = it.template + sample.format = format + } } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFingerprintSampleConverter.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFingerprintSampleConverter.kt index a3e1317978..83a6da7e05 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFingerprintSampleConverter.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmFingerprintSampleConverter.kt @@ -1,22 +1,32 @@ package com.simprints.infra.enrolment.records.repository.local.models import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.infra.enrolment.records.realm.store.models.DbFingerprintSample as RealmFingerprintSample -internal fun RealmFingerprintSample.toDomain(): Sample = Sample( - id = id, - identifier = DbSampleIdentifier.fromId(fingerIdentifier).toDomain(), - template = template, - format = format, - referenceId = referenceId, - modality = Modality.FINGERPRINT, -) +internal fun List.toDomain(): List = groupBy { it.referenceId } + .map { (referenceId, templates) -> + BiometricReference( + referenceId = referenceId, + format = templates.first().format, + modality = Modality.FINGERPRINT, + templates = templates.map { + BiometricTemplate( + id = it.id, + template = it.template, + identifier = DbTemplateIdentifier.fromId(it.fingerIdentifier).toDomain(), + ) + }, + ) + } -internal fun Sample.toRealmFingerprintDb(): RealmFingerprintSample = RealmFingerprintSample().also { sample -> - sample.id = id - sample.referenceId = referenceId - sample.fingerIdentifier = identifier.fromDomain()?.id ?: -1 - sample.template = template - sample.format = format +internal fun BiometricReference.toRealmFingerprintDb(): List = templates.map { + RealmFingerprintSample().also { sample -> + sample.id = it.id + sample.referenceId = referenceId + sample.fingerIdentifier = it.identifier.fromDomain()?.id ?: -1 + sample.template = it.template + sample.format = format + } } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmSubjectConverter.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmSubjectConverter.kt index 3b9eb6ee0b..c9b3fe4e97 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmSubjectConverter.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RealmSubjectConverter.kt @@ -2,40 +2,37 @@ package com.simprints.infra.enrolment.records.repository.local.models import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.domain.tokenization.isTokenized import com.simprints.infra.enrolment.records.realm.store.models.DbExternalCredential -import com.simprints.infra.enrolment.records.realm.store.models.DbFaceSample -import com.simprints.infra.enrolment.records.realm.store.models.DbFingerprintSample import com.simprints.infra.enrolment.records.realm.store.models.toDate import com.simprints.infra.enrolment.records.realm.store.models.toRealmInstant -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import io.realm.kotlin.ext.toRealmList import io.realm.kotlin.types.RealmUUID import com.simprints.infra.enrolment.records.realm.store.models.DbSubject as RealmSubject -internal fun RealmSubject.toDomain(): Subject { +internal fun RealmSubject.toDomain(): EnrolmentRecord { val attendantId = if (isAttendantIdTokenized) attendantId.asTokenizableEncrypted() else attendantId.asTokenizableRaw() val moduleId = if (isModuleIdTokenized) moduleId.asTokenizableEncrypted() else moduleId.asTokenizableRaw() - return Subject( + return EnrolmentRecord( subjectId = subjectId.toString(), projectId = projectId, attendantId = attendantId, moduleId = moduleId, createdAt = createdAt?.toDate(), updatedAt = updatedAt?.toDate(), - samples = fingerprintSamples.map(DbFingerprintSample::toDomain) + - faceSamples.map(DbFaceSample::toDomain), + references = fingerprintSamples.toDomain() + faceSamples.toDomain(), externalCredentials = externalCredentials.map(DbExternalCredential::toDomain), ) } -internal fun Subject.toRealmDb(): RealmSubject = RealmSubject().also { subject -> +internal fun EnrolmentRecord.toRealmDb(): RealmSubject = RealmSubject().also { subject -> subject.subjectId = RealmUUID.from(subjectId) subject.projectId = projectId subject.attendantId = attendantId.value @@ -46,12 +43,12 @@ internal fun Subject.toRealmDb(): RealmSubject = RealmSubject().also { subject - subject.isAttendantIdTokenized = attendantId.isTokenized() subject.externalCredentials = externalCredentials.map(ExternalCredential::toRealmDb).toRealmList() - subject.fingerprintSamples = samples + subject.fingerprintSamples = references .filter { it.modality == Modality.FINGERPRINT } - .map(Sample::toRealmFingerprintDb) + .flatMap(BiometricReference::toRealmFingerprintDb) .toRealmList() - subject.faceSamples = samples + subject.faceSamples = references .filter { it.modality == Modality.FACE } - .map(Sample::toRealmFaceDb) + .flatMap(BiometricReference::toRealmFaceDb) .toRealmList() } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomBiometricTemplateConverter.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomBiometricTemplateConverter.kt index 90e297ae3a..c9c5eb491b 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomBiometricTemplateConverter.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomBiometricTemplateConverter.kt @@ -1,26 +1,40 @@ package com.simprints.infra.enrolment.records.repository.local.models -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.infra.enrolment.records.room.store.models.DbBiometricTemplate import com.simprints.infra.enrolment.records.room.store.models.DbModality import com.simprints.infra.enrolment.records.room.store.models.fromDomain import com.simprints.infra.enrolment.records.room.store.models.toDomain -internal fun DbBiometricTemplate.toSample(): Sample = Sample( - id = uuid, - identifier = identifier?.let { DbSampleIdentifier.fromId(it) }.toDomain(), - modality = DbModality.fromId(modality).toDomain(), - referenceId = referenceId, - format = format, - template = templateData, -) +// Biometric reference data is stored as part of the biometric template data, so we group them back in the domain model +internal fun List.toBiometricReferences(): List = groupBy { it.referenceId } + .filter { (_, templates) -> templates.isNotEmpty() } + .map { (referenceId, templates) -> + val firstTemplate = templates.first() -internal fun Sample.toRoomDb(subjectId: String): DbBiometricTemplate = DbBiometricTemplate( - uuid = id, - templateData = template, - format = format, - identifier = identifier.fromDomain()?.id, - subjectId = subjectId, - referenceId = referenceId, - modality = modality.fromDomain().id, -) + BiometricReference( + referenceId = referenceId, + format = firstTemplate.format, + modality = DbModality.fromId(firstTemplate.modality).toDomain(), + templates = templates.map { + BiometricTemplate( + id = it.uuid, + template = it.templateData, + identifier = DbTemplateIdentifier.fromId(it.identifier).toDomain(), + ) + }, + ) + } + +internal fun BiometricReference.toRoomDbTemplate(subjectId: String): List = templates.map { + DbBiometricTemplate( + uuid = it.id, + templateData = it.template, + format = format, + identifier = it.identifier.fromDomain()?.id, + subjectId = subjectId, + referenceId = referenceId, + modality = modality.fromDomain().id, + ) +} diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomExternalCredentialConverter.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomExternalCredentialConverter.kt index b12caae012..d6a1fdbfe6 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomExternalCredentialConverter.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomExternalCredentialConverter.kt @@ -9,13 +9,12 @@ internal fun DbExternalCredential.toDomain(): ExternalCredential = ExternalCrede id = id, value = value.asTokenizableEncrypted(), subjectId = subjectId, - type = ExternalCredentialType.valueOf(type) + type = ExternalCredentialType.valueOf(type), ) -internal fun ExternalCredential.toRoomDb(): DbExternalCredential = DbExternalCredential( +internal fun ExternalCredential.toRoomDbCredentials(): DbExternalCredential = DbExternalCredential( id = id, value = value.value, subjectId = subjectId, - type = type.name + type = type.name, ) - diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomSubjectConverter.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomSubjectConverter.kt index a14111bdd9..9baf08089a 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomSubjectConverter.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/local/models/RoomSubjectConverter.kt @@ -1,18 +1,18 @@ package com.simprints.infra.enrolment.records.repository.local.models import com.simprints.core.domain.tokenization.asTokenizableEncrypted -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.enrolment.records.room.store.models.SubjectBiometrics import java.util.Date -internal fun SubjectBiometrics.toDomain() = Subject( - subjectId = subject.subjectId.toString(), +internal fun SubjectBiometrics.toDomain() = EnrolmentRecord( + subjectId = subject.subjectId, projectId = subject.projectId, attendantId = subject.attendantId.asTokenizableEncrypted(), moduleId = subject.moduleId.asTokenizableEncrypted(), createdAt = subject.createdAt?.toDate(), updatedAt = subject.updatedAt?.toDate(), - samples = biometricTemplates.map { it.toSample() }, + references = biometricTemplates.toBiometricReferences(), externalCredentials = externalCredentials.map { it.toDomain() }, ) diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSource.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSource.kt index 5af42f94fa..f10b66dd7e 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSource.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSource.kt @@ -1,7 +1,7 @@ package com.simprints.infra.enrolment.records.repository.remote -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord fun interface EnrolmentRecordRemoteDataSource { - suspend fun uploadRecords(subjects: List) + suspend fun uploadRecords(enrolmentRecords: List) } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImpl.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImpl.kt index a0b6e8effc..a1753b015d 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImpl.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImpl.kt @@ -3,7 +3,7 @@ package com.simprints.infra.enrolment.records.repository.remote import com.simprints.core.tools.utils.EncodingUtils import com.simprints.core.tools.utils.EncodingUtilsImpl import com.simprints.infra.authstore.AuthStore -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.enrolment.records.repository.remote.models.ApiEnrolmentRecords import com.simprints.infra.enrolment.records.repository.remote.models.toEnrolmentRecord import com.simprints.infra.network.SimNetwork @@ -16,13 +16,13 @@ internal class EnrolmentRecordRemoteDataSourceImpl( @Inject constructor(authStore: AuthStore) : this(authStore, EncodingUtilsImpl) - override suspend fun uploadRecords(subjects: List) { + override suspend fun uploadRecords(enrolmentRecords: List) { val projectId = authStore.signedInProjectId return getClient().executeCall { apiInterface -> apiInterface.uploadRecords( projectId, - ApiEnrolmentRecords(subjects.map { it.toEnrolmentRecord(encoder) }), + ApiEnrolmentRecords(enrolmentRecords.map { it.toEnrolmentRecord(encoder) }), ) } } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/ApiEnrolmentRecord.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/ApiEnrolmentRecord.kt index cf1a0cd678..5df2b7f829 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/ApiEnrolmentRecord.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/ApiEnrolmentRecord.kt @@ -2,9 +2,9 @@ package com.simprints.infra.enrolment.records.repository.remote.models import androidx.annotation.Keep import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference import com.simprints.core.tools.utils.EncodingUtils -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.enrolment.records.repository.remote.models.face.toFaceApi import com.simprints.infra.enrolment.records.repository.remote.models.fingerprint.toFingerprintApi @@ -16,19 +16,19 @@ internal data class ApiEnrolmentRecord( val biometricReferences: List, ) -internal fun Subject.toEnrolmentRecord(encoder: EncodingUtils): ApiEnrolmentRecord = ApiEnrolmentRecord( +internal fun EnrolmentRecord.toEnrolmentRecord(encoder: EncodingUtils): ApiEnrolmentRecord = ApiEnrolmentRecord( subjectId, moduleId.value, attendantId.value, - buildBiometricReferences(samples, encoder), + buildBiometricReferences(references, encoder), ) internal fun buildBiometricReferences( - samples: List, + references: List, encoder: EncodingUtils, -): List = samples.groupBy { it.modality }.mapNotNull { (modality, modalitySamples) -> - when (modality) { - Modality.FINGERPRINT -> modalitySamples.toFingerprintApi(encoder) - Modality.FACE -> modalitySamples.toFaceApi(encoder) +): List = references.mapNotNull { reference -> + when (reference.modality) { + Modality.FINGERPRINT -> reference.toFingerprintApi(encoder) + Modality.FACE -> reference.toFaceApi(encoder) } } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/face/ApiFaceReference.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/face/ApiFaceReference.kt index b8ee1f1c54..1e2871f6a8 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/face/ApiFaceReference.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/face/ApiFaceReference.kt @@ -3,7 +3,7 @@ package com.simprints.infra.enrolment.records.repository.remote.models.face import androidx.annotation.Keep import com.fasterxml.jackson.annotation.JsonTypeInfo import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference import com.simprints.core.tools.utils.EncodingUtils import com.simprints.infra.enrolment.records.repository.remote.models.ApiBiometricReference @@ -20,11 +20,11 @@ internal data class ApiFaceReference( val metadata: HashMap? = null, ) : ApiBiometricReference(ApiBiometricReferenceType.FaceReference) -internal fun List.toFaceApi(encoder: EncodingUtils): ApiFaceReference? = if (isNotEmpty()) { +internal fun BiometricReference.toFaceApi(encoder: EncodingUtils): ApiFaceReference? = if (templates.isNotEmpty()) { ApiFaceReference( - first().referenceId, - map { ApiFaceTemplate(encoder.byteArrayToBase64(it.template)) }, - first().format, + referenceId, + templates.map { ApiFaceTemplate(encoder.byteArrayToBase64(it.template)) }, + format, ) } else { null diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFinger.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFinger.kt index 7c76a0eee5..e980642876 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFinger.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFinger.kt @@ -1,7 +1,7 @@ package com.simprints.infra.enrolment.records.repository.remote.models.fingerprint import androidx.annotation.Keep -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier @Keep internal enum class ApiFinger { @@ -17,16 +17,16 @@ internal enum class ApiFinger { LEFT_5TH_FINGER, } -internal fun SampleIdentifier.toApi(): ApiFinger = when (this) { - SampleIdentifier.RIGHT_5TH_FINGER -> ApiFinger.RIGHT_5TH_FINGER - SampleIdentifier.RIGHT_4TH_FINGER -> ApiFinger.RIGHT_4TH_FINGER - SampleIdentifier.RIGHT_3RD_FINGER -> ApiFinger.RIGHT_3RD_FINGER - SampleIdentifier.RIGHT_INDEX_FINGER -> ApiFinger.RIGHT_INDEX_FINGER - SampleIdentifier.RIGHT_THUMB -> ApiFinger.RIGHT_THUMB - SampleIdentifier.LEFT_THUMB -> ApiFinger.LEFT_THUMB - SampleIdentifier.LEFT_INDEX_FINGER -> ApiFinger.LEFT_INDEX_FINGER - SampleIdentifier.LEFT_3RD_FINGER -> ApiFinger.LEFT_3RD_FINGER - SampleIdentifier.LEFT_4TH_FINGER -> ApiFinger.LEFT_4TH_FINGER - SampleIdentifier.LEFT_5TH_FINGER -> ApiFinger.LEFT_5TH_FINGER - SampleIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") +internal fun TemplateIdentifier.toApi(): ApiFinger = when (this) { + TemplateIdentifier.RIGHT_5TH_FINGER -> ApiFinger.RIGHT_5TH_FINGER + TemplateIdentifier.RIGHT_4TH_FINGER -> ApiFinger.RIGHT_4TH_FINGER + TemplateIdentifier.RIGHT_3RD_FINGER -> ApiFinger.RIGHT_3RD_FINGER + TemplateIdentifier.RIGHT_INDEX_FINGER -> ApiFinger.RIGHT_INDEX_FINGER + TemplateIdentifier.RIGHT_THUMB -> ApiFinger.RIGHT_THUMB + TemplateIdentifier.LEFT_THUMB -> ApiFinger.LEFT_THUMB + TemplateIdentifier.LEFT_INDEX_FINGER -> ApiFinger.LEFT_INDEX_FINGER + TemplateIdentifier.LEFT_3RD_FINGER -> ApiFinger.LEFT_3RD_FINGER + TemplateIdentifier.LEFT_4TH_FINGER -> ApiFinger.LEFT_4TH_FINGER + TemplateIdentifier.LEFT_5TH_FINGER -> ApiFinger.LEFT_5TH_FINGER + TemplateIdentifier.NONE -> throw IllegalArgumentException("Must be a finger sample identifier") } diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerprintReference.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerprintReference.kt index 58ad01c6ff..0c4b55a107 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerprintReference.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerprintReference.kt @@ -3,7 +3,7 @@ package com.simprints.infra.enrolment.records.repository.remote.models.fingerpri import androidx.annotation.Keep import com.fasterxml.jackson.annotation.JsonTypeInfo import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference import com.simprints.core.tools.utils.EncodingUtils import com.simprints.infra.enrolment.records.repository.remote.models.ApiBiometricReference @@ -20,16 +20,13 @@ internal data class ApiFingerprintReference( val metadata: HashMap? = null, ) : ApiBiometricReference(ApiBiometricReferenceType.FingerprintReference) -internal fun List.toFingerprintApi(encoder: EncodingUtils): ApiFingerprintReference? = if (isNotEmpty()) { +internal fun BiometricReference.toFingerprintApi(encoder: EncodingUtils): ApiFingerprintReference? = if (templates.isNotEmpty()) { ApiFingerprintReference( - first().referenceId, - map { - ApiFingerprintTemplate( - encoder.byteArrayToBase64(it.template), - it.identifier.toApi(), - ) + referenceId, + templates.map { + ApiFingerprintTemplate(encoder.byteArrayToBase64(it.template), it.identifier.toApi()) }, - first().format, + format, ) } else { null diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImplTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImplTest.kt index f23befbb6f..3bd04938bc 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImplTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/EnrolmentRecordRepositoryImplTest.kt @@ -1,7 +1,7 @@ package com.simprints.infra.enrolment.records.repository import android.content.SharedPreferences -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.time.Timestamp @@ -9,10 +9,10 @@ import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.local.EnrolmentRecordLocalDataSource import com.simprints.infra.enrolment.records.repository.local.SelectEnrolmentRecordLocalDataSourceUseCase import com.simprints.infra.enrolment.records.repository.local.migration.InsertRecordsInRoomDuringMigrationUseCase @@ -38,19 +38,19 @@ class EnrolmentRecordRepositoryImplTest { private const val SUBJECT_ID_3 = "SUBJECT_ID_3" private const val SUBJECT_ID_4 = "SUBJECT_ID_4" private const val SUBJECT_ID_5 = "SUBJECT_ID_5" - private val SUBJECT_1 = mockk { + private val ENROLMENT_RECORD_1 = mockk { every { subjectId } returns SUBJECT_ID_1 } - private val SUBJECT_2 = mockk { + private val ENROLMENT_RECORD_2 = mockk { every { subjectId } returns SUBJECT_ID_2 } - private val SUBJECT_3 = mockk { + private val ENROLMENT_RECORD_3 = mockk { every { subjectId } returns SUBJECT_ID_3 } - private val SUBJECT_4 = mockk { + private val ENROLMENT_RECORD_4 = mockk { every { subjectId } returns SUBJECT_ID_4 } - private val SUBJECT_5 = mockk { + private val ENROLMENT_RECORD_5 = mockk { every { subjectId } returns SUBJECT_ID_5 } } @@ -59,7 +59,7 @@ class EnrolmentRecordRepositoryImplTest { private val tokenizationProcessor = mockk() private val localDataSource = mockk(relaxed = true) private val selectEnrolmentRecordLocalDataSource = mockk() - private val commCareDataSource = mockk(relaxed = true) + private val commCareDataSource = mockk(relaxed = true) private val remoteDataSource = mockk(relaxed = true) private val prefsEditor = mockk(relaxed = true) private val prefs = mockk { @@ -92,55 +92,55 @@ class EnrolmentRecordRepositoryImplTest { @Test fun `should upload the records correctly when there is more than one batch`() = runTest { - val expectedSubjectQuery = SubjectQuery(sort = true) + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery(sort = true) every { prefs.getString(any(), null) } returns null - coEvery { localDataSource.load(expectedSubjectQuery) } returns listOf( - SUBJECT_1, - SUBJECT_2, - SUBJECT_3, + coEvery { localDataSource.load(expectedEnrolmentRecordQuery) } returns listOf( + ENROLMENT_RECORD_1, + ENROLMENT_RECORD_2, + ENROLMENT_RECORD_3, ) repository.uploadRecords(listOf()) - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_1, SUBJECT_2)) } - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_3)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_1, ENROLMENT_RECORD_2)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_3)) } coVerify(exactly = 1) { prefsEditor.putString(any(), SUBJECT_ID_2) } coVerify(exactly = 1) { prefsEditor.remove(any()) } } @Test fun `should upload the records correctly when there is exactly one batch`() = runTest { - val expectedSubjectQuery = SubjectQuery(sort = true) + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery(sort = true) every { prefs.getString(any(), null) } returns null - coEvery { localDataSource.load(expectedSubjectQuery) } returns listOf( - SUBJECT_1, - SUBJECT_2, + coEvery { localDataSource.load(expectedEnrolmentRecordQuery) } returns listOf( + ENROLMENT_RECORD_1, + ENROLMENT_RECORD_2, ) repository.uploadRecords(listOf()) - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_1, SUBJECT_2)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_1, ENROLMENT_RECORD_2)) } coVerify(exactly = 1) { prefsEditor.putString(any(), SUBJECT_ID_2) } coVerify(exactly = 1) { prefsEditor.remove(any()) } } @Test fun `should upload the records correctly when there is more than two batches`() = runTest { - val expectedSubjectQuery = SubjectQuery(sort = true) + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery(sort = true) every { prefs.getString(any(), null) } returns null - coEvery { localDataSource.load(expectedSubjectQuery) } returns listOf( - SUBJECT_1, - SUBJECT_2, - SUBJECT_3, - SUBJECT_4, - SUBJECT_5, + coEvery { localDataSource.load(expectedEnrolmentRecordQuery) } returns listOf( + ENROLMENT_RECORD_1, + ENROLMENT_RECORD_2, + ENROLMENT_RECORD_3, + ENROLMENT_RECORD_4, + ENROLMENT_RECORD_5, ) repository.uploadRecords(listOf()) - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_1, SUBJECT_2)) } - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_3, SUBJECT_4)) } - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_5)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_1, ENROLMENT_RECORD_2)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_3, ENROLMENT_RECORD_4)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_5)) } coVerify(exactly = 1) { prefsEditor.putString(any(), SUBJECT_ID_2) } coVerify(exactly = 1) { prefsEditor.putString(any(), SUBJECT_ID_4) } coVerify(exactly = 1) { prefsEditor.remove(any()) } @@ -148,36 +148,36 @@ class EnrolmentRecordRepositoryImplTest { @Test fun `should upload the records correctly when some subject ids are specified`() = runTest { - val expectedSubjectQuery = - SubjectQuery(sort = true, subjectIds = listOf(SUBJECT_ID_1, SUBJECT_ID_2)) + val expectedEnrolmentRecordQuery = + EnrolmentRecordQuery(sort = true, subjectIds = listOf(SUBJECT_ID_1, SUBJECT_ID_2)) every { prefs.getString(any(), null) } returns null - coEvery { localDataSource.load(expectedSubjectQuery) } returns listOf( - SUBJECT_1, - SUBJECT_2, + coEvery { localDataSource.load(expectedEnrolmentRecordQuery) } returns listOf( + ENROLMENT_RECORD_1, + ENROLMENT_RECORD_2, ) repository.uploadRecords(listOf(SUBJECT_ID_1, SUBJECT_ID_2)) - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_1, SUBJECT_2)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_1, ENROLMENT_RECORD_2)) } coVerify(exactly = 1) { prefsEditor.putString(any(), SUBJECT_ID_2) } coVerify(exactly = 1) { prefsEditor.remove(any()) } } @Test fun `should upload the records correctly when it has failed before`() = runTest { - val expectedSubjectQuery = SubjectQuery( + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery( sort = true, afterSubjectId = SUBJECT_ID_3, ) every { prefs.getString(any(), null) } returns SUBJECT_ID_3 - coEvery { localDataSource.load(expectedSubjectQuery) } returns listOf( - SUBJECT_1, - SUBJECT_2, + coEvery { localDataSource.load(expectedEnrolmentRecordQuery) } returns listOf( + ENROLMENT_RECORD_1, + ENROLMENT_RECORD_2, ) repository.uploadRecords(listOf()) - coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(SUBJECT_1, SUBJECT_2)) } + coVerify(exactly = 1) { remoteDataSource.uploadRecords(listOf(ENROLMENT_RECORD_1, ENROLMENT_RECORD_2)) } coVerify(exactly = 1) { prefsEditor.putString(any(), SUBJECT_ID_2) } coVerify(exactly = 1) { prefsEditor.remove(any()) } } @@ -191,17 +191,17 @@ class EnrolmentRecordRepositoryImplTest { val attendantIdTokenized = "attendantId".asTokenizableEncrypted() val moduleIdTokenized = "moduleId".asTokenizableEncrypted() val project = mockk() - val subject = Subject( + val enrolmentRecord = EnrolmentRecord( subjectId = "subjectId", projectId = projectId, attendantId = attendantIdRaw, moduleId = moduleIdRaw, createdAt = Date(), updatedAt = null, - samples = emptyList(), + references = emptyList(), ) every { project.id } returns projectId - coEvery { localDataSource.load(any()) } returns listOf(subject) + coEvery { localDataSource.load(any()) } returns listOf(enrolmentRecord) every { tokenizationProcessor.encrypt( decrypted = attendantIdRaw, @@ -218,12 +218,12 @@ class EnrolmentRecordRepositoryImplTest { } returns moduleIdTokenized repository.tokenizeExistingRecords(project) - val expectedSubject = subject.copy( + val expectedSubject = enrolmentRecord.copy( attendantId = attendantIdTokenized, moduleId = moduleIdTokenized, ) - val expectedSubjectActions = listOf(SubjectAction.Creation(expectedSubject)) - coVerify { localDataSource.performActions(expectedSubjectActions, project) } + val expectedEnrolmentRecordActions = listOf(EnrolmentRecordAction.Creation(expectedSubject)) + coVerify { localDataSource.performActions(expectedEnrolmentRecordActions, project) } } @Test @@ -232,17 +232,17 @@ class EnrolmentRecordRepositoryImplTest { val attendantIdRaw = "attendantId".asTokenizableRaw() val moduleIdRaw = "moduleId".asTokenizableRaw() val project = mockk() - val subject = Subject( + val enrolmentRecord = EnrolmentRecord( subjectId = "subjectId", projectId = "another project id", attendantId = attendantIdRaw, moduleId = moduleIdRaw, createdAt = Date(), updatedAt = null, - samples = emptyList(), + references = emptyList(), ) every { project.id } returns projectId - coEvery { localDataSource.load(any()) } returns listOf(subject) + coEvery { localDataSource.load(any()) } returns listOf(enrolmentRecord) repository.tokenizeExistingRecords(project) coVerify { localDataSource.performActions(emptyList(), project) } @@ -260,34 +260,34 @@ class EnrolmentRecordRepositoryImplTest { @Test fun `should return the correct count of subjects when dataSource is Simprints`() = runTest { - val expectedSubjectQuery = SubjectQuery() - coEvery { localDataSource.count(expectedSubjectQuery) } returns 5 + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery() + coEvery { localDataSource.count(expectedEnrolmentRecordQuery) } returns 5 - val count = repository.count(query = expectedSubjectQuery, dataSource = BiometricDataSource.Simprints) + val count = repository.count(query = expectedEnrolmentRecordQuery, dataSource = BiometricDataSource.Simprints) assert(count == 5) - coVerify(exactly = 1) { localDataSource.count(expectedSubjectQuery) } + coVerify(exactly = 1) { localDataSource.count(expectedEnrolmentRecordQuery) } } @Test fun `should return the correct count of subjects when dataSource is CommCare`() = runTest { - val expectedSubjectQuery = SubjectQuery() - coEvery { commCareDataSource.count(expectedSubjectQuery, any()) } returns 5 + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery() + coEvery { commCareDataSource.count(expectedEnrolmentRecordQuery, any()) } returns 5 - val count = repository.count(query = expectedSubjectQuery, dataSource = BiometricDataSource.CommCare("")) + val count = repository.count(query = expectedEnrolmentRecordQuery, dataSource = BiometricDataSource.CommCare("")) assert(count == 5) - coVerify(exactly = 1) { commCareDataSource.count(expectedSubjectQuery, any()) } + coVerify(exactly = 1) { commCareDataSource.count(expectedEnrolmentRecordQuery, any()) } } @Test fun `should forward the call to the local data source when loading fingerprint identities and dataSource is Simprints`() = runTest { - val expectedSubjectQuery = SubjectQuery() + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery() val expectedRange = listOf(0..10) - val expectedFingerprintIdentities = listOf() + val expectedFingerprintIdentities = listOf() coEvery { - localDataSource.loadIdentities( - expectedSubjectQuery, + localDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -296,10 +296,10 @@ class EnrolmentRecordRepositoryImplTest { ) } returns createTestChannel(expectedFingerprintIdentities) - val fingerprintIdentities = mutableListOf() + val fingerprintIdentities = mutableListOf() repository - .loadIdentities( - query = expectedSubjectQuery, + .loadCandidateRecords( + query = expectedEnrolmentRecordQuery, ranges = expectedRange, dataSource = BiometricDataSource.Simprints, project = project, @@ -311,8 +311,8 @@ class EnrolmentRecordRepositoryImplTest { assert(fingerprintIdentities == expectedFingerprintIdentities) coVerify(exactly = 1) { - localDataSource.loadIdentities( - expectedSubjectQuery, + localDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -324,12 +324,12 @@ class EnrolmentRecordRepositoryImplTest { @Test fun `should forward the call to the commcare data source when loading fingerprint identities and dataSource is CommCare`() = runTest { - val expectedSubjectQuery = SubjectQuery() + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery() val expectedRange = listOf(0..10) - val expectedFingerprintIdentities = listOf() + val expectedFingerprintIdentities = listOf() coEvery { - commCareDataSource.loadIdentities( - expectedSubjectQuery, + commCareDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -337,10 +337,10 @@ class EnrolmentRecordRepositoryImplTest { onCandidateLoaded, ) } returns createTestChannel(expectedFingerprintIdentities) - val fingerprintIdentities = mutableListOf() + val fingerprintIdentities = mutableListOf() repository - .loadIdentities( - query = expectedSubjectQuery, + .loadCandidateRecords( + query = expectedEnrolmentRecordQuery, ranges = expectedRange, dataSource = BiometricDataSource.CommCare(""), project = project, @@ -352,8 +352,8 @@ class EnrolmentRecordRepositoryImplTest { assert(fingerprintIdentities == expectedFingerprintIdentities) coVerify(exactly = 1) { - commCareDataSource.loadIdentities( - expectedSubjectQuery, + commCareDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -365,12 +365,12 @@ class EnrolmentRecordRepositoryImplTest { @Test fun `should forward the call to the local data source when loading face identities and dataSource is Simprints`() = runTest { - val expectedSubjectQuery = SubjectQuery() + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery() val expectedRange = listOf(0..10) - val expectedFaceIdentities = listOf() + val expectedFaceIdentities = listOf() coEvery { - localDataSource.loadIdentities( - expectedSubjectQuery, + localDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -379,10 +379,10 @@ class EnrolmentRecordRepositoryImplTest { ) } returns createTestChannel(expectedFaceIdentities) - val faceIdentities = mutableListOf() + val faceIdentities = mutableListOf() repository - .loadIdentities( - query = expectedSubjectQuery, + .loadCandidateRecords( + query = expectedEnrolmentRecordQuery, ranges = expectedRange, dataSource = BiometricDataSource.Simprints, project = project, @@ -394,8 +394,8 @@ class EnrolmentRecordRepositoryImplTest { assert(faceIdentities == expectedFaceIdentities) coVerify(exactly = 1) { - localDataSource.loadIdentities( - expectedSubjectQuery, + localDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -407,12 +407,12 @@ class EnrolmentRecordRepositoryImplTest { @Test fun `should forward the call to the commcare data source when loading face identities and dataSource is CommCare`() = runTest { - val expectedSubjectQuery = SubjectQuery() + val expectedEnrolmentRecordQuery = EnrolmentRecordQuery() val expectedRange = listOf(0..10) - val expectedFaceIdentities = listOf() + val expectedFaceIdentities = listOf() coEvery { - commCareDataSource.loadIdentities( - expectedSubjectQuery, + commCareDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -421,11 +421,11 @@ class EnrolmentRecordRepositoryImplTest { ) } returns createTestChannel(expectedFaceIdentities) - val faceIdentities = mutableListOf() + val faceIdentities = mutableListOf() repository - .loadIdentities( - query = expectedSubjectQuery, + .loadCandidateRecords( + query = expectedEnrolmentRecordQuery, ranges = expectedRange, dataSource = BiometricDataSource.CommCare(""), project = project, @@ -437,8 +437,8 @@ class EnrolmentRecordRepositoryImplTest { assert(faceIdentities == expectedFaceIdentities) coVerify(exactly = 1) { - commCareDataSource.loadIdentities( - expectedSubjectQuery, + commCareDataSource.loadCandidateRecords( + expectedEnrolmentRecordQuery, expectedRange, any(), project, @@ -448,13 +448,13 @@ class EnrolmentRecordRepositoryImplTest { } } - private fun createTestChannel(vararg lists: List): ReceiveChannel { - val channel = Channel(lists.size) + private fun createTestChannel(vararg lists: List): ReceiveChannel { + val channel = Channel(lists.size) runBlocking { var time = 0L for (list in lists) { channel.send( - IdentityBatch( + CandidateRecordBatch( identities = list, loadingStartTime = Timestamp(time++), loadingEndTime = Timestamp(time++), @@ -469,10 +469,10 @@ class EnrolmentRecordRepositoryImplTest { @Test fun `performActions should forward the subject creation calls to the insertRecordsDuringMigration`() = runTest { val actions = listOf( - mockk(), - mockk(), - mockk(), - mockk(), + mockk(), + mockk(), + mockk(), + mockk(), ) coJustRun { insertRecordsDuringMigration.invoke(any(), any()) } repository.performActions(actions, mockk()) diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareIdentityDataSourceTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareCandidateRecordDataSourceTest.kt similarity index 72% rename from infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareIdentityDataSourceTest.kt rename to infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareCandidateRecordDataSourceTest.kt index 4eb85ffd59..d97ae224e1 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareIdentityDataSourceTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/commcare/CommCareCandidateRecordDataSourceTest.kt @@ -5,20 +5,21 @@ import android.content.Context import android.database.Cursor import android.net.Uri import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier.LEFT_INDEX_FINGER -import com.simprints.core.domain.sample.SampleIdentifier.LEFT_THUMB +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.reference.CandidateRecord +import com.simprints.core.domain.common.TemplateIdentifier.LEFT_INDEX_FINGER +import com.simprints.core.domain.common.TemplateIdentifier.LEFT_THUMB import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.tools.json.JsonHelper import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.utils.EncodingUtils import com.simprints.core.tools.utils.ExtractCommCareCaseIdUseCase import com.simprints.infra.config.store.models.Project -import com.simprints.infra.enrolment.records.repository.commcare.CommCareIdentityDataSource.Companion.COLUMN_DATUM_ID -import com.simprints.infra.enrolment.records.repository.commcare.CommCareIdentityDataSource.Companion.COLUMN_VALUE +import com.simprints.infra.enrolment.records.repository.commcare.CommCareCandidateRecordDataSource.Companion.COLUMN_DATUM_ID +import com.simprints.infra.enrolment.records.repository.commcare.CommCareCandidateRecordDataSource.Companion.COLUMN_VALUE import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.usecases.CompareImplicitTokenizedStringsUseCase import com.simprints.infra.logging.Simber import com.simprints.testtools.common.coroutines.TestCoroutineRule @@ -34,80 +35,90 @@ import org.junit.BeforeClass import org.junit.Rule import org.junit.Test -class CommCareIdentityDataSourceTest { - companion object { +class CommCareCandidateRecordDataSourceTest { + companion object Companion { private const val SUBJECT_ACTIONS_FINGERPRINT_1 = """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"b26c91bc-b307-4131-80c3-55090ba5dbf2","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"quality":99,"template":"123","finger":"LEFT_THUMB"},{"quality":88,"template":"123","finger":"LEFT_INDEX_FINGER"}],"format":"ISO_19794_2","type":"FINGERPRINT_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" private const val SUBJECT_ACTIONS_FINGERPRINT_2 = - """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"a961fcb4-8573-4270-a1b2-088e88275b00","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"quality":77,"template":"123","finger":"LEFT_THUMB"},{"quality":66,"template":"123","finger":"LEFT_INDEX_FINGER"}],"format":"NEC_1_5","type":"FINGERPRINT_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" + """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"a961fcb4-8573-4270-a1b2-088e88275b00","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"0e4b0028-be90-46f3-a5cb-5ea581478b24","templates":[{"quality":77,"template":"123","finger":"LEFT_THUMB"},{"quality":66,"template":"123","finger":"LEFT_INDEX_FINGER"}],"format":"NEC_1_5","type":"FINGERPRINT_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" private const val SUBJECT_ACTIONS_FACE_1 = - """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"b26c91bc-b307-4131-80c3-55090ba5dbf2","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"template":"123"}],"format":"ROC_1_23","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" + """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"b26c91bc-b307-4131-80c3-55090ba5dbf2","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"82902131-d753-447e-a600-6d3efa5df8b0","templates":[{"template":"123"}],"format":"ROC_1_23","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" private const val SUBJECT_ACTIONS_FACE_2 = - """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"a961fcb4-8573-4270-a1b2-088e88275b00","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"template":"123"}],"format":"ROC_3","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" + """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"a961fcb4-8573-4270-a1b2-088e88275b00","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"c7f4571f-2591-4c9d-bbd3-56dfdb82d206","templates":[{"template":"123"}],"format":"ROC_3","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" private const val SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_1 = - """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"b26c91bc-b307-4131-80c3-55090ba5dbf2","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"quality":99,"template":"123","finger":"LEFT_THUMB"},{"quality":88,"template":"123","finger":"LEFT_INDEX_FINGER"}],"format":"ISO_19794_2","type":"FINGERPRINT_REFERENCE"},{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"template":"123"}],"format":"ROC_1_23","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" + """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"b26c91bc-b307-4131-80c3-55090ba5dbf2","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"quality":99,"template":"123","finger":"LEFT_THUMB"},{"quality":88,"template":"123","finger":"LEFT_INDEX_FINGER"}],"format":"ISO_19794_2","type":"FINGERPRINT_REFERENCE"},{"id":"82902131-d753-447e-a600-6d3efa5df8b0","templates":[{"template":"123"}],"format":"ROC_1_23","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" private const val SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_2 = - """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"a961fcb4-8573-4270-a1b2-088e88275b00","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"quality":77,"template":"123","finger":"LEFT_THUMB"},{"quality":66,"template":"123","finger":"LEFT_INDEX_FINGER"}],"format":"NEC_1_5","type":"FINGERPRINT_REFERENCE"},{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"template":"123"}],"format":"ROC_3","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" + """{"events":[{"id":"0dafcd03-96c4-4ca5-b802-292da6d4f799","payload":{"subjectId":"a961fcb4-8573-4270-a1b2-088e88275b00","projectId":"nXcj9neYhXP9rFp56uWk","moduleId":{"value":"AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="},"attendantId":{"value":"AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="},"biometricReferences":[{"id":"0e4b0028-be90-46f3-a5cb-5ea581478b24","templates":[{"quality":77,"template":"123","finger":"LEFT_THUMB"},{"quality":66,"template":"123","finger":"LEFT_INDEX_FINGER"}],"format":"NEC_1_5","type":"FINGERPRINT_REFERENCE"},{"id":"c7f4571f-2591-4c9d-bbd3-56dfdb82d206","templates":[{"template":"123"}],"format":"ROC_3","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" - private val expectedFingerprintIdentities = listOf( - Identity( + private val expectedFingerprintCandidates = listOf( + CandidateRecord( subjectId = "b26c91bc-b307-4131-80c3-55090ba5dbf2", - samples = listOf( - Sample( - identifier = LEFT_THUMB, - template = byteArrayOf(), + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = LEFT_THUMB, + template = byteArrayOf(), + ), + BiometricTemplate( + identifier = LEFT_INDEX_FINGER, + template = byteArrayOf(), + ), + ), format = "ISO_19794_2", - referenceId = "referenceId", - modality = Modality.FINGERPRINT, - ), - Sample( - identifier = LEFT_INDEX_FINGER, - template = byteArrayOf(), - format = "ISO_19794_2", - referenceId = "referenceId", + referenceId = "2b9b4991-29d7-3eee-ac02-191afaa0c1a2", modality = Modality.FINGERPRINT, ), ), ), - Identity( + CandidateRecord( subjectId = "a961fcb4-8573-4270-a1b2-088e88275b00", - samples = listOf( - Sample( - identifier = LEFT_THUMB, - template = byteArrayOf(), - format = "ISO_19794_2", - referenceId = "referenceId", - modality = Modality.FINGERPRINT, - ), - Sample( - identifier = LEFT_INDEX_FINGER, - template = byteArrayOf(), - format = "ISO_19794_2", - referenceId = "referenceId", + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = LEFT_THUMB, + template = byteArrayOf(), + ), + BiometricTemplate( + identifier = LEFT_INDEX_FINGER, + template = byteArrayOf(), + ), + ), + format = "NEC_1_5", + referenceId = "0e4b0028-be90-46f3-a5cb-5ea581478b24", modality = Modality.FINGERPRINT, ), ), ), ) - val expectedFaceIdentities = listOf( - Identity( + val expectedFaceCandidates = listOf( + CandidateRecord( subjectId = "b26c91bc-b307-4131-80c3-55090ba5dbf2", - samples = listOf( - Sample( - template = byteArrayOf(), + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(), + ), + ), format = "ROC_1_23", - referenceId = "referenceId", + referenceId = "82902131-d753-447e-a600-6d3efa5df8b0", modality = Modality.FACE, ), ), ), - Identity( + CandidateRecord( subjectId = "a961fcb4-8573-4270-a1b2-088e88275b00", - samples = listOf( - Sample( - template = byteArrayOf(), + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(), + ), + ), format = "ROC_3", - referenceId = "referenceId", + referenceId = "c7f4571f-2591-4c9d-bbd3-56dfdb82d206", modality = Modality.FACE, ), ), @@ -170,7 +181,7 @@ class CommCareIdentityDataSourceTest { private lateinit var mockDataCursor: Cursor - private lateinit var dataSource: CommCareIdentityDataSource + private lateinit var dataSource: CommCareCandidateRecordDataSource @MockK lateinit var project: Project @@ -217,7 +228,7 @@ class CommCareIdentityDataSourceTest { every { useCase.invoke(any(), any(), any(), any()) } returns true every { extractCommCareCaseIdUseCase.invoke(any()) } returns null - dataSource = CommCareIdentityDataSource( + dataSource = CommCareCandidateRecordDataSource( timeHelper, encoder, JsonHelper, @@ -248,112 +259,110 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getString(1) } returnsMany subjectActionsData } - private fun assertFingerprintIdentitiesMatch( - expected: List, - actual: List, + private fun assertFingerprintCandidatesMatch( + expected: List, + actual: List, templateFormat: String, ) { val areContentsEqual = expected - .filter { identity -> identity.samples.any { it.format == templateFormat } } + .filter { candidateRecord -> candidateRecord.references.any { it.format == templateFormat } } .zip(actual) { exp, act -> exp.subjectId == act.subjectId && - exp.samples - .zip(act.samples) { expSample, actSample -> - expSample.identifier == actSample.identifier && - expSample.template.contentEquals(actSample.template) && - expSample.format == actSample.format + exp.references + .zip(act.references) { expReference, actReference -> + expReference.templates == actReference.templates && expReference.format == actReference.format }.all { it } }.all { it } assertTrue(areContentsEqual) } - private fun assertFaceIdentitiesMatch( - expected: List, - actual: List, + private fun assertFaceCandidatesMatch( + expected: List, + actual: List, templateFormat: String, ) { val areContentsEqual = expected - .filter { identity -> identity.samples.any { it.format == templateFormat } } + .filter { candidateRecord -> candidateRecord.references.any { it.format == templateFormat } } .zip(actual) { exp, act -> exp.subjectId == act.subjectId && - exp.samples - .zip(act.samples) { expSample, actSample -> - expSample.template.contentEquals(actSample.template) && - expSample.format == actSample.format + exp.references + .zip(act.references) { expReference, actReference -> + expReference.templates == actReference.templates && expReference.format == actReference.format }.all { it } }.all { it } assertTrue(areContentsEqual) } @Test - fun `test loadIdentities with fingerprint records`() = runTest { - setupMetadataCursor(expectedFingerprintIdentities.size, listOf(true, false)) + fun `test loadCandidateRecords with fingerprint records`() = runTest { + setupMetadataCursor(expectedFingerprintCandidates.size, listOf(true, false)) setupDataCursor(listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2)) val templateFormat = "ISO_19794_2" - val actualIdentities = mutableListOf() + val actualCandidates = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(format = templateFormat), - ranges = listOf(0..expectedFingerprintIdentities.size), + .loadCandidateRecords( + query = EnrolmentRecordQuery(format = templateFormat), + ranges = listOf(0..expectedFingerprintCandidates.size), project = project, dataSource = commCareBiometricDataSource, scope = this, onCandidateLoaded = {}, ).consumeEach { - actualIdentities.addAll(it.identities) + actualCandidates.addAll(it.identities) } - assertEquals(1, actualIdentities.size) - assertFingerprintIdentitiesMatch(expectedFingerprintIdentities, actualIdentities, templateFormat) + val expected = expectedFingerprintCandidates + assertEquals(1, actualCandidates.size) + assertFingerprintCandidatesMatch(expected, actualCandidates, templateFormat) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } @Test - fun `test loadIdentities with face records`() = runTest { - setupMetadataCursor(expectedFaceIdentities.size, listOf(true, false)) + fun `test loadCandidateRecords with face records`() = runTest { + setupMetadataCursor(expectedFaceCandidates.size, listOf(true, false)) setupDataCursor(listOf(SUBJECT_ACTIONS_FACE_1, SUBJECT_ACTIONS_FACE_2)) val templateFormat = "ROC_1_23" - val actualIdentities = mutableListOf() + val actualCandidates = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery( + .loadCandidateRecords( + query = EnrolmentRecordQuery( format = templateFormat, attendantId = TokenizableString.Tokenized("AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzSr9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88="), moduleId = TokenizableString.Tokenized("AWuA3H0WGtHI2uod+ePZ3yiWTt9etQ=="), subjectId = "b26c91bc-b307-4131-80c3-55090ba5dbf2", ), - ranges = listOf(0..expectedFaceIdentities.size), + ranges = listOf(0..expectedFaceCandidates.size), project = project, dataSource = commCareBiometricDataSource, scope = this, onCandidateLoaded = {}, ).consumeEach { - actualIdentities.addAll(it.identities) + actualCandidates.addAll(it.identities) } - assertEquals(1, actualIdentities.size) - assertFaceIdentitiesMatch(expectedFaceIdentities, actualIdentities, templateFormat) + assertEquals(1, actualCandidates.size) + assertFaceCandidatesMatch(expectedFaceCandidates, actualCandidates, templateFormat) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } @Test - fun `test loadIdentities returns only identities with fingerprint references`() = runTest { - setupMetadataCursor(expectedFingerprintIdentities.size + 1, listOf(true, true, false)) + fun `test loadCandidateRecords returns only identities with fingerprint references`() = runTest { + setupMetadataCursor(expectedFingerprintCandidates.size + 1, listOf(true, true, false)) setupDataCursor(listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2, SUBJECT_ACTIONS_FACE_1)) val templateFormat = "NEC_1_5" - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(format = templateFormat), - ranges = listOf(0..expectedFingerprintIdentities.size), + .loadCandidateRecords( + query = EnrolmentRecordQuery(format = templateFormat), + ranges = listOf(0..expectedFingerprintCandidates.size), project = project, dataSource = commCareBiometricDataSource, scope = this, @@ -363,23 +372,23 @@ class CommCareIdentityDataSourceTest { } assertEquals(1, actualIdentities.size) - assertFingerprintIdentitiesMatch(expectedFingerprintIdentities, actualIdentities, templateFormat) + assertFingerprintCandidatesMatch(expectedFingerprintCandidates, actualIdentities, templateFormat) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } @Test - fun `test loadIdentities returns only identities with face references`() = runTest { - setupMetadataCursor(expectedFaceIdentities.size + 1, listOf(true, true, false)) + fun `test loadCandidateRecords returns only identities with face references`() = runTest { + setupMetadataCursor(expectedFaceCandidates.size + 1, listOf(true, true, false)) setupDataCursor(listOf(SUBJECT_ACTIONS_FACE_1, SUBJECT_ACTIONS_FACE_2, SUBJECT_ACTIONS_FINGERPRINT_1)) val templateFormat = "ROC_1_23" - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(format = templateFormat), - ranges = listOf(0..expectedFaceIdentities.size), + .loadCandidateRecords( + query = EnrolmentRecordQuery(format = templateFormat), + ranges = listOf(0..expectedFaceCandidates.size), project = project, dataSource = commCareBiometricDataSource, scope = this, @@ -389,23 +398,23 @@ class CommCareIdentityDataSourceTest { } assertEquals(1, actualIdentities.size) - assertFaceIdentitiesMatch(expectedFaceIdentities, actualIdentities, templateFormat) + assertFaceCandidatesMatch(expectedFaceCandidates, actualIdentities, templateFormat) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } @Test - fun `test loadIdentities returns only fingerprint references for dual modality identities`() = runTest { - setupMetadataCursor(expectedFingerprintIdentities.size, listOf(true, false)) + fun `test loadCandidateRecords returns only fingerprint references for dual modality identities`() = runTest { + setupMetadataCursor(expectedFingerprintCandidates.size, listOf(true, false)) setupDataCursor(listOf(SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_1, SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_2)) val templateFormat = "ISO_19794_2" - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(format = templateFormat), - ranges = listOf(0..expectedFingerprintIdentities.size), + .loadCandidateRecords( + query = EnrolmentRecordQuery(format = templateFormat), + ranges = listOf(0..expectedFingerprintCandidates.size), project = project, dataSource = commCareBiometricDataSource, scope = this, @@ -415,23 +424,23 @@ class CommCareIdentityDataSourceTest { } assertEquals(1, actualIdentities.size) - assertFingerprintIdentitiesMatch(expectedFingerprintIdentities, actualIdentities, templateFormat) + assertFingerprintCandidatesMatch(expectedFingerprintCandidates, actualIdentities, templateFormat) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } @Test - fun `test loadIdentities returns only face references for dual modality identities`() = runTest { - setupMetadataCursor(expectedFaceIdentities.size, listOf(true, false)) + fun `test loadCandidateRecords returns only face references for dual modality identities`() = runTest { + setupMetadataCursor(expectedFaceCandidates.size, listOf(true, false)) setupDataCursor(listOf(SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_1, SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_2)) val templateFormat = "ROC_1_23" - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(format = templateFormat), - ranges = listOf(0..expectedFaceIdentities.size), + .loadCandidateRecords( + query = EnrolmentRecordQuery(format = templateFormat), + ranges = listOf(0..expectedFaceCandidates.size), project = project, dataSource = commCareBiometricDataSource, scope = this, @@ -441,7 +450,7 @@ class CommCareIdentityDataSourceTest { } assertEquals(1, actualIdentities.size) - assertFaceIdentitiesMatch(expectedFaceIdentities, actualIdentities, templateFormat) + assertFaceCandidatesMatch(expectedFaceCandidates, actualIdentities, templateFormat) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } @@ -451,7 +460,7 @@ class CommCareIdentityDataSourceTest { val expectedCount = 5 every { mockMetadataCursor.count } returns expectedCount - val query = SubjectQuery() + val query = EnrolmentRecordQuery() val actualCount = dataSource.count(query) assertEquals(expectedCount, actualCount) @@ -471,11 +480,11 @@ class CommCareIdentityDataSourceTest { ) } returns null - val query = SubjectQuery() + val query = EnrolmentRecordQuery() val range = 0..0 - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( + .loadCandidateRecords( query = query, ranges = listOf(range), project = project, @@ -495,11 +504,11 @@ class CommCareIdentityDataSourceTest { fun `test metadata cursor size below range's first`() = runTest { every { mockMetadataCursor.count } returns 1 - val query = SubjectQuery() + val query = EnrolmentRecordQuery() val range = 2..3 - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( + .loadCandidateRecords( query = query, ranges = listOf(range), project = project, @@ -517,17 +526,17 @@ class CommCareIdentityDataSourceTest { @Test fun `test metadata cursor size bigger than range`() = runTest { - setupMetadataCursor(expectedFingerprintIdentities.size + 1, listOf(true, true)) + setupMetadataCursor(expectedFingerprintCandidates.size + 1, listOf(true, true)) every { mockMetadataCursor.position } returnsMany listOf(1, 2) setupDataCursor(listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2)) val templateFormat = "ISO_19794_2" - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(format = templateFormat), - ranges = listOf(expectedFingerprintIdentities.indices), + .loadCandidateRecords( + query = EnrolmentRecordQuery(format = templateFormat), + ranges = listOf(expectedFingerprintCandidates.indices), project = project, dataSource = commCareBiometricDataSource, scope = this, @@ -537,7 +546,7 @@ class CommCareIdentityDataSourceTest { } assertEquals(1, actualIdentities.size) - assertFingerprintIdentitiesMatch(expectedFingerprintIdentities, actualIdentities, templateFormat) + assertFingerprintCandidatesMatch(expectedFingerprintCandidates, actualIdentities, templateFormat) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } @@ -548,10 +557,10 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.moveToNext() } returns true every { mockMetadataCursor.getString(any()) } returns null - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(), + .loadCandidateRecords( + query = EnrolmentRecordQuery(), ranges = listOf(0..2), project = project, dataSource = commCareBiometricDataSource, @@ -570,10 +579,10 @@ class CommCareIdentityDataSourceTest { fun `exception during metadata cursor access is reported`() = runTest { every { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } throws RuntimeException("Some exception") - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(), + .loadCandidateRecords( + query = EnrolmentRecordQuery(), ranges = listOf(0..2), project = project, dataSource = commCareBiometricDataSource, @@ -594,10 +603,10 @@ class CommCareIdentityDataSourceTest { setupMetadataCursor(2, listOf(true, false)) every { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } returns null - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(), + .loadCandidateRecords( + query = EnrolmentRecordQuery(), ranges = listOf(0..2), project = project, dataSource = commCareBiometricDataSource, @@ -621,10 +630,10 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getString(0) } returns "someKey" every { mockDataCursor.getString(1) } returns "someValue" - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(), + .loadCandidateRecords( + query = EnrolmentRecordQuery(), ranges = listOf(0..2), project = project, dataSource = commCareBiometricDataSource, @@ -644,10 +653,10 @@ class CommCareIdentityDataSourceTest { setupMetadataCursor(2, listOf(true, false)) setupDataCursor(listOf("invalid JSON 1", "invalid JSON 2")) - val actualIdentities = mutableListOf() + val actualIdentities = mutableListOf() dataSource - .loadIdentities( - query = SubjectQuery(), + .loadCandidateRecords( + query = EnrolmentRecordQuery(), ranges = listOf(0..2), project = project, dataSource = commCareBiometricDataSource, @@ -675,7 +684,7 @@ class CommCareIdentityDataSourceTest { ) } returns null - val query = SubjectQuery() + val query = EnrolmentRecordQuery() val actualCount = dataSource.count(query) assertEquals(0, actualCount) @@ -687,7 +696,7 @@ class CommCareIdentityDataSourceTest { val testCaseId = "test-case-id" every { extractCommCareCaseIdUseCase.invoke(any()) } returns testCaseId - val query = SubjectQuery(metadata = "test-metadata") + val query = EnrolmentRecordQuery(metadata = "test-metadata") val actualCount = dataSource.count(query, commCareBiometricDataSource) assertEquals(1, actualCount) @@ -701,7 +710,7 @@ class CommCareIdentityDataSourceTest { every { extractCommCareCaseIdUseCase.invoke(any()) } returns null every { mockMetadataCursor.count } returns expectedCount - val query = SubjectQuery(metadata = "test-metadata") + val query = EnrolmentRecordQuery(metadata = "test-metadata") val actualCount = dataSource.count(query, commCareBiometricDataSource) assertEquals(expectedCount, actualCount) @@ -709,7 +718,7 @@ class CommCareIdentityDataSourceTest { } @Test - fun `loadIdentities with case ID calls onCandidateLoaded`() = runTest { + fun `loadCandidateRecords with case ID calls onCandidateLoaded`() = runTest { val testCaseId = "test-case-id" var onCandidateLoadedCalled = false every { extractCommCareCaseIdUseCase.invoke(any()) } returns testCaseId @@ -719,9 +728,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getString(0) } returnsMany listOf("someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returns SUBJECT_ACTIONS_FINGERPRINT_1 - val query = SubjectQuery(metadata = "test-metadata") + val query = EnrolmentRecordQuery(metadata = "test-metadata") dataSource - .loadIdentities( + .loadCandidateRecords( query = query, ranges = listOf(0..1), project = project, diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceTest.kt index 3acb0c6973..61a3b4833a 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RealmEnrolmentRecordLocalDataSourceTest.kt @@ -4,9 +4,10 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.reference.CandidateRecord +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.time.TimeHelper @@ -18,9 +19,9 @@ import com.simprints.infra.enrolment.records.realm.store.models.DbFaceSample import com.simprints.infra.enrolment.records.realm.store.models.DbFingerprintSample import com.simprints.infra.enrolment.records.realm.store.models.DbSubject import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.repository.local.RealmEnrolmentRecordLocalDataSource.Companion.EXTERNAL_CREDENTIAL_FIELD import com.simprints.infra.enrolment.records.repository.local.RealmEnrolmentRecordLocalDataSource.Companion.FACE_SAMPLES_FIELD import com.simprints.infra.enrolment.records.repository.local.RealmEnrolmentRecordLocalDataSource.Companion.FINGERPRINT_SAMPLES_FIELD @@ -72,7 +73,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { private lateinit var blockCapture: CapturingSlot Any> private lateinit var mutableBlockCapture: CapturingSlot<(MutableRealm) -> Any> private val onCandidateLoaded: suspend () -> Unit = {} - private var localSubjects: MutableList = mutableListOf() + private var localEnrolmentRecords: MutableList = mutableListOf() private lateinit var enrolmentRecordLocalDataSource: RealmEnrolmentRecordLocalDataSource @@ -80,13 +81,13 @@ class RealmEnrolmentRecordLocalDataSourceTest { @Before fun setup() { MockKAnnotations.init(this, relaxed = true) - localSubjects = mutableListOf() + localEnrolmentRecords = mutableListOf() val insertedSubject = slot() - every { mutableRealm.delete(any()) } answers { localSubjects.clear() } - every { mutableRealm.deleteAll() } answers { localSubjects.clear() } + every { mutableRealm.delete(any()) } answers { localEnrolmentRecords.clear() } + every { mutableRealm.deleteAll() } answers { localEnrolmentRecords.clear() } every { mutableRealm.copyToRealm(capture(insertedSubject), any()) } answers { - localSubjects.add(insertedSubject.captured.toDomain()) + localEnrolmentRecords.add(insertedSubject.captured.toDomain()) insertedSubject.captured } @@ -99,7 +100,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { mutableBlockCapture.captured.invoke(mutableRealm) } every { realmQuery.count() } answers { - mockk { every { find() } returns localSubjects.size.toLong() } + mockk { every { find() } returns localEnrolmentRecords.size.toLong() } } every { realm.query(DbSubject::class) } returns realmQuery @@ -145,18 +146,18 @@ class RealmEnrolmentRecordLocalDataSourceTest { val savedPersons = saveFakePeople(getRandomPeople(20)) val fakePerson = savedPersons[0].toRealmDb() - val people = mutableListOf() + val candidates = mutableListOf() enrolmentRecordLocalDataSource - .loadIdentities( - SubjectQuery(), + .loadCandidateRecords( + EnrolmentRecordQuery(), listOf(IntRange(0, 20)), BiometricDataSource.Simprints, project, this, onCandidateLoaded, - ).consumeEach { people.addAll(it.identities) } + ).consumeEach { candidates.addAll(it.identities) } - listOf(fakePerson).zip(people).forEach { (subject, identity) -> + listOf(fakePerson).zip(candidates).forEach { (subject, identity) -> assertThat(subject.subjectId).isEqualTo(identity.subjectId) } } @@ -166,8 +167,8 @@ class RealmEnrolmentRecordLocalDataSourceTest { val format = "SupportedFormat" enrolmentRecordLocalDataSource - .loadIdentities( - SubjectQuery(format = format), + .loadCandidateRecords( + EnrolmentRecordQuery(format = format), listOf(IntRange(0, 20)), BiometricDataSource.Simprints, project, @@ -188,19 +189,19 @@ class RealmEnrolmentRecordLocalDataSourceTest { val savedPersons = saveFakePeople(getRandomPeople(20)) val fakePerson = savedPersons[0].toRealmDb() - val people = mutableListOf() + val candidates = mutableListOf() enrolmentRecordLocalDataSource - .loadIdentities( - SubjectQuery(), + .loadCandidateRecords( + EnrolmentRecordQuery(), listOf(IntRange(0, 20)), BiometricDataSource.Simprints, project, this, onCandidateLoaded, ).consumeEach { - people.addAll(it.identities) + candidates.addAll(it.identities) } - listOf(fakePerson).zip(people).forEach { (subject, identity) -> + listOf(fakePerson).zip(candidates).forEach { (subject, identity) -> assertThat(subject.subjectId).isEqualTo(identity.subjectId) } } @@ -210,7 +211,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { val fakePerson = getFakePerson() saveFakePerson(fakePerson) - val people = enrolmentRecordLocalDataSource.load(SubjectQuery()).toList() + val people = enrolmentRecordLocalDataSource.load(EnrolmentRecordQuery()).toList() listOf(fakePerson).zip(people).forEach { (dbSubject, subject) -> assertThat(dbSubject.deepEquals(subject.toRealmDb())).isTrue() @@ -222,7 +223,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { val savedPersons = saveFakePeople(getRandomPeople(20)) val fakePerson = savedPersons[0].toRealmDb() - val people = enrolmentRecordLocalDataSource.load(SubjectQuery(attendantId = savedPersons[0].attendantId)).toList() + val people = enrolmentRecordLocalDataSource.load(EnrolmentRecordQuery(attendantId = savedPersons[0].attendantId)).toList() listOf(fakePerson).zip(people).forEach { (dbSubject, subject) -> assertThat(dbSubject.deepEquals(subject.toRealmDb())).isTrue() } @@ -233,7 +234,10 @@ class RealmEnrolmentRecordLocalDataSourceTest { val savedPersons = saveFakePeople(getRandomPeople(20)) val fakePerson = savedPersons[0].toRealmDb() - val people = enrolmentRecordLocalDataSource.load(SubjectQuery(moduleId = fakePerson.moduleId.asTokenizableEncrypted())).toList() + val people = enrolmentRecordLocalDataSource + .load( + EnrolmentRecordQuery(moduleId = fakePerson.moduleId.asTokenizableEncrypted()), + ).toList() listOf(fakePerson).zip(people).forEach { (dbSubject, subject) -> assertThat(dbSubject.deepEquals(subject.toRealmDb())).isTrue() } @@ -245,7 +249,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { every { realmSingleQuery.find() } returns null enrolmentRecordLocalDataSource.performActions( - listOf(SubjectAction.Creation(subject.toDomain())), + listOf(EnrolmentRecordAction.Creation(subject.toDomain())), project, ) val peopleCount = enrolmentRecordLocalDataSource.count() @@ -258,17 +262,17 @@ class RealmEnrolmentRecordLocalDataSourceTest { val fingerReferenceId = "fingerToDelete" every { realmSingleQuery.find() } returns getRandomSubject( faceSamples = listOf( - getRandomFaceSample(referenceId = faceReferenceId), + getRandomFaceReference(referenceId = faceReferenceId), ), fingerprintSamples = listOf( - getRandomFingerprintSample(referenceId = fingerReferenceId), + getRandomFingerprintReference(referenceId = fingerReferenceId), ), ).toRealmDb() val subject = getFakePerson() enrolmentRecordLocalDataSource.performActions( - listOf(SubjectAction.Creation(subject.toDomain())), + listOf(EnrolmentRecordAction.Creation(subject.toDomain())), project, ) @@ -287,20 +291,20 @@ class RealmEnrolmentRecordLocalDataSourceTest { val fingerReferenceId = "fingerToDelete" every { realmSingleQuery.find() } returns getRandomSubject( faceSamples = listOf( - getRandomFaceSample(referenceId = faceReferenceId), - getRandomFaceSample(), + getRandomFaceReference(referenceId = faceReferenceId), + getRandomFaceReference(), ), fingerprintSamples = listOf( - getRandomFingerprintSample(referenceId = fingerReferenceId), - getRandomFingerprintSample(), + getRandomFingerprintReference(referenceId = fingerReferenceId), + getRandomFingerprintReference(), ), ).toRealmDb() enrolmentRecordLocalDataSource.performActions( listOf( - SubjectAction.Update( + EnrolmentRecordAction.Update( subject.subjectId.toString(), - samplesToAdd = listOf(getRandomFaceSample(), getRandomFingerprintSample()), + samplesToAdd = listOf(getRandomFaceReference(), getRandomFingerprintReference()), referenceIdsToRemove = listOf(faceReferenceId, fingerReferenceId), externalCredentialsToAdd = listOf(), externalCredentialIdsToRemove = listOf(), @@ -331,8 +335,8 @@ class RealmEnrolmentRecordLocalDataSourceTest { fun performSubjectUpdateExternalActions() = runTest { val subject = getFakePerson() every { realmSingleQuery.find() } returns getRandomSubject( - faceSamples = listOf(getRandomFaceSample()), - fingerprintSamples = listOf(getRandomFingerprintSample()), + faceSamples = listOf(getRandomFaceReference()), + fingerprintSamples = listOf(getRandomFingerprintReference()), externalCredentials = listOf( getRandomExternalCredential("id1"), getRandomExternalCredential("id2"), @@ -341,7 +345,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { enrolmentRecordLocalDataSource.performActions( listOf( - SubjectAction.Update( + EnrolmentRecordAction.Update( subject.subjectId.toString(), samplesToAdd = listOf(), referenceIdsToRemove = listOf(), @@ -369,7 +373,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { val subject = getFakePerson() saveFakePerson(subject) enrolmentRecordLocalDataSource.performActions( - listOf(SubjectAction.Deletion(subject.subjectId.toString())), + listOf(EnrolmentRecordAction.Deletion(subject.subjectId.toString())), project, ) val peopleCount = enrolmentRecordLocalDataSource.count() @@ -441,7 +445,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { ) enrolmentRecordLocalDataSource.load( - SubjectQuery(externalCredential = credentialValue.asTokenizableEncrypted()), + EnrolmentRecordQuery(externalCredential = credentialValue.asTokenizableEncrypted()), ) verify { @@ -454,9 +458,11 @@ class RealmEnrolmentRecordLocalDataSourceTest { private fun getFakePerson(): DbSubject = getRandomSubject().toRealmDb() - private fun saveFakePerson(fakeSubject: DbSubject): DbSubject = fakeSubject.also { localSubjects.add(it.toDomain()) } + private fun saveFakePerson(fakeSubject: DbSubject): DbSubject = fakeSubject.also { localEnrolmentRecords.add(it.toDomain()) } - private fun saveFakePeople(subjects: List): List = subjects.toMutableList().also { localSubjects.addAll(it) } + private fun saveFakePeople(enrolmentRecords: List): List = enrolmentRecords.toMutableList().also { + localEnrolmentRecords.addAll(it) + } private fun DbSubject.deepEquals(other: DbSubject): Boolean = when { this.subjectId != other.subjectId -> false @@ -468,7 +474,7 @@ class RealmEnrolmentRecordLocalDataSourceTest { else -> true } - private fun getRandomPeople(numberOfPeople: Int): ArrayList = arrayListOf().also { list -> + private fun getRandomPeople(numberOfPeople: Int): ArrayList = arrayListOf().also { list -> repeat(numberOfPeople) { list.add(getRandomSubject(UUID.randomUUID().toString())) } @@ -479,42 +485,50 @@ class RealmEnrolmentRecordLocalDataSourceTest { projectId: String = UUID.randomUUID().toString(), userId: String = UUID.randomUUID().toString(), moduleId: String = UUID.randomUUID().toString(), - faceSamples: List = listOf( - getRandomFaceSample(), - getRandomFaceSample(), + faceSamples: List = listOf( + getRandomFaceReference(), + getRandomFaceReference(), ), - fingerprintSamples: List = listOf(), + fingerprintSamples: List = listOf(), externalCredentials: List = listOf( getRandomExternalCredential(), ), - ): Subject = Subject( + ): EnrolmentRecord = EnrolmentRecord( subjectId = patientId, projectId = projectId, attendantId = userId.asTokenizableRaw(), moduleId = moduleId.asTokenizableRaw(), - samples = fingerprintSamples + faceSamples, + references = fingerprintSamples + faceSamples, externalCredentials = externalCredentials, ) - private fun getRandomFaceSample( + private fun getRandomFaceReference( id: String = UUID.randomUUID().toString(), referenceId: String = "referenceId", - ) = Sample( - id = id, + ) = BiometricReference( referenceId = referenceId, - template = Random.nextBytes(64), + templates = listOf( + BiometricTemplate( + id = id, + template = Random.nextBytes(64), + ), + ), format = "faceTemplateFormat", modality = Modality.FACE, ) - private fun getRandomFingerprintSample( + private fun getRandomFingerprintReference( id: String = UUID.randomUUID().toString(), referenceId: String = "referenceId", - ) = Sample( - id = id, + ) = BiometricReference( referenceId = referenceId, - identifier = SampleIdentifier.LEFT_3RD_FINGER, - template = Random.nextBytes(64), + templates = listOf( + BiometricTemplate( + id = id, + identifier = TemplateIdentifier.LEFT_3RD_FINGER, + template = Random.nextBytes(64), + ), + ), format = "fingerprintTemplateFormat", modality = Modality.FINGERPRINT, ) diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSourceTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSourceTest.kt index 5a1e75b04c..b93dbca17e 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSourceTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordLocalDataSourceTest.kt @@ -5,16 +5,17 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.tools.time.TimeHelper import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource.Simprints -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.room.store.SubjectsDatabase import com.simprints.infra.enrolment.records.room.store.SubjectsDatabase.Companion.SUBJECT_DB_VERSION import com.simprints.infra.enrolment.records.room.store.SubjectsDatabaseFactory @@ -72,114 +73,141 @@ class RoomEnrolmentRecordLocalDataSourceTest { private val date = Date() // Use a fixed date for consistent timestamps in tests // Samples defined first - private val faceSample1 = Sample( - template = byteArrayOf(1, 2, 3), + private val faceSample1 = BiometricReference( + templates = listOf( + BiometricTemplate( + id = "face-uuid-1", + template = byteArrayOf(1, 2, 3), + ), + ), format = ROC_1_FORMAT, referenceId = "ref-face-1", - id = "face-uuid-1", modality = Modality.FACE, ) - private val faceSample2 = Sample( - template = byteArrayOf(4, 5, 6), + private val faceSample2 = BiometricReference( + templates = listOf( + BiometricTemplate( + id = "face-uuid-2", + template = byteArrayOf(4, 5, 6), + ), + ), format = ROC_3_FORMAT, referenceId = "ref-face-2", - id = "face-uuid-2", modality = Modality.FACE, ) - private val faceSample3 = Sample( - template = byteArrayOf(7, 8, 9), + private val faceSample3 = BiometricReference( + templates = listOf( + BiometricTemplate( + id = "face-uuid-3-p2", + template = byteArrayOf(7, 8, 9), + ), + ), format = ROC_1_FORMAT, referenceId = "ref-face-3-p2", - id = "face-uuid-3-p2", modality = Modality.FACE, ) - private val fingerprintSample1 = Sample( - identifier = SampleIdentifier.LEFT_THUMB, - template = byteArrayOf(10, 11), + private val fingerprintSample1 = BiometricReference( + templates = listOf( + BiometricTemplate( + id = "fp-uuid-1", + identifier = TemplateIdentifier.LEFT_THUMB, + template = byteArrayOf(10, 11), + ), + ), format = NEC_FORMAT, referenceId = "ref-fp-1", - id = "fp-uuid-1", modality = Modality.FINGERPRINT, ) - private val fingerprintSample2 = Sample( - identifier = SampleIdentifier.RIGHT_THUMB, - template = byteArrayOf(12, 13), + + private val fingerprintSample2 = BiometricReference( + templates = listOf( + BiometricTemplate( + id = "fp-uuid-2", + identifier = TemplateIdentifier.RIGHT_THUMB, + template = byteArrayOf(12, 13), + ), + ), format = ISO_FORMAT, referenceId = "ref-fp-2", - id = "fp-uuid-2", modality = Modality.FINGERPRINT, ) - private val fingerprintSample3 = Sample( - identifier = SampleIdentifier.LEFT_INDEX_FINGER, - template = byteArrayOf(14, 15), + + private val fingerprintSample3 = BiometricReference( + templates = listOf( + BiometricTemplate( + id = "fp-uuid-3-p2", + identifier = TemplateIdentifier.LEFT_INDEX_FINGER, + template = byteArrayOf(14, 15), + ), + ), format = NEC_FORMAT, referenceId = "ref-fp-3-p2", - id = "fp-uuid-3-p2", modality = Modality.FINGERPRINT, ) // Subjects defined using the samples - private val subject1P1WithFace = Subject( + private val enrolmentRecord1P1WithFace = EnrolmentRecord( subjectId = "subj-001", projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_1_ID, - samples = listOf(faceSample1), + references = listOf(faceSample1), createdAt = date, updatedAt = date, externalCredentials = listOf(getExternalCredential("subj-001")), ) - private val subject2P1WithFinger = Subject( + private val enrolmentRecord2P1WithFinger = EnrolmentRecord( subjectId = "subj-002", projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_1_ID, - samples = listOf(fingerprintSample1), + references = listOf(fingerprintSample1), createdAt = date, updatedAt = date, externalCredentials = listOf(getExternalCredential("subj-002")), ) - private val subject3P1WithBoth = Subject( + private val enrolmentRecord3P1WithBoth = EnrolmentRecord( subjectId = "subj-003", projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_2_ID, - samples = listOf(faceSample2, fingerprintSample2), + references = listOf(faceSample2, fingerprintSample2), externalCredentials = listOf(getExternalCredential("subj-003")), ) - private val subject4P2WithBoth = Subject( + private val enrolmentRecord4P2WithBoth = EnrolmentRecord( subjectId = "subj-004", projectId = PROJECT_2_ID, attendantId = ATTENDANT_2_ID, moduleId = MODULE_2_ID, - samples = listOf(faceSample3, fingerprintSample3), + references = listOf(faceSample3, fingerprintSample3), createdAt = date, updatedAt = date, externalCredentials = listOf(getExternalCredential("subj-004")), ) - private val subject5P2WithFace = Subject( + private val enrolmentRecord5P2WithFace = EnrolmentRecord( // Added subject subjectId = "subj-005", projectId = PROJECT_2_ID, attendantId = ATTENDANT_2_ID, moduleId = MODULE_3_ID, - samples = listOf(faceSample3.copy(id = UUID.randomUUID().toString())), + references = listOf(faceSample3.copyWithTemplateId(UUID.randomUUID().toString())), createdAt = Date(date.time + 1000), // Slightly different time updatedAt = Date(date.time + 1000), externalCredentials = listOf(getExternalCredential("subj-005")), ) - private val subject6P2WithFinger = Subject( + + private val enrolmentRecord6P2WithFinger = EnrolmentRecord( // Added subject subjectId = "subj-006", projectId = PROJECT_2_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_3_ID, - samples = listOf(fingerprintSample3.copy(id = UUID.randomUUID().toString())), + references = listOf(fingerprintSample3.copyWithTemplateId(UUID.randomUUID().toString())), createdAt = Date(date.time + 2000), // Different time updatedAt = Date(date.time + 2000), externalCredentials = listOf(getExternalCredential("subj-006")), ) - private val subjectInvalidNoSamples = Subject( + private val enrolmentRecordInvalidNoSamples = EnrolmentRecord( subjectId = "subj-invalid", projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, @@ -221,12 +249,12 @@ class RoomEnrolmentRecordLocalDataSourceTest { } private val initialData = listOf( - SubjectAction.Creation(subject1P1WithFace), - SubjectAction.Creation(subject2P1WithFinger), - SubjectAction.Creation(subject3P1WithBoth), - SubjectAction.Creation(subject4P2WithBoth), - SubjectAction.Creation(subject5P2WithFace), - SubjectAction.Creation(subject6P2WithFinger), + EnrolmentRecordAction.Creation(enrolmentRecord1P1WithFace), + EnrolmentRecordAction.Creation(enrolmentRecord2P1WithFinger), + EnrolmentRecordAction.Creation(enrolmentRecord3P1WithBoth), + EnrolmentRecordAction.Creation(enrolmentRecord4P2WithBoth), + EnrolmentRecordAction.Creation(enrolmentRecord5P2WithFace), + EnrolmentRecordAction.Creation(enrolmentRecord6P2WithFinger), ) private fun getExternalCredential(subjectId: String) = ExternalCredential( @@ -248,18 +276,18 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `performActions - Creation - should succeed with face sample`() = runTest { // Given - val action = SubjectAction.Creation(subject1P1WithFace) + val action = EnrolmentRecordAction.Creation(enrolmentRecord1P1WithFace) // When dataSource.performActions(listOf(action), project) // Then - val loaded = dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)) assertThat(loaded).hasSize(1) val createdSubject = loaded[0] - assertThat(createdSubject.subjectId).isEqualTo(subject1P1WithFace.subjectId) - assertThat(createdSubject.samples).hasSize(1) - assertThat(createdSubject.samples).containsExactly(faceSample1) + assertThat(createdSubject.subjectId).isEqualTo(enrolmentRecord1P1WithFace.subjectId) + assertThat(createdSubject.references).hasSize(1) + assertThat(createdSubject.references).containsExactly(faceSample1) assertThat(createdSubject.createdAt).isNotNull() assertThat(createdSubject.updatedAt).isNotNull() } @@ -267,18 +295,18 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `performActions - Creation - should succeed with fingerprint sample`() = runTest { // Given - val action = SubjectAction.Creation(subject2P1WithFinger) + val action = EnrolmentRecordAction.Creation(enrolmentRecord2P1WithFinger) // When dataSource.performActions(listOf(action), project) // Then - val loaded = dataSource.load(SubjectQuery(subjectId = subject2P1WithFinger.subjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord2P1WithFinger.subjectId)) assertThat(loaded).hasSize(1) val createdSubject = loaded[0] - assertThat(createdSubject.subjectId).isEqualTo(subject2P1WithFinger.subjectId) - assertThat(createdSubject.samples).hasSize(1) - assertThat(createdSubject.samples).containsExactly(fingerprintSample1) + assertThat(createdSubject.subjectId).isEqualTo(enrolmentRecord2P1WithFinger.subjectId) + assertThat(createdSubject.references).hasSize(1) + assertThat(createdSubject.references).containsExactly(fingerprintSample1) assertThat(createdSubject.createdAt).isNotNull() assertThat(createdSubject.updatedAt).isNotNull() } @@ -286,24 +314,24 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `performActions - Creation - should succeed with both samples`() = runTest { // Given: - val action = SubjectAction.Creation(subject3P1WithBoth) + val action = EnrolmentRecordAction.Creation(enrolmentRecord3P1WithBoth) // When dataSource.performActions(listOf(action), project) // Then - val loaded = dataSource.load(SubjectQuery(subjectId = subject3P1WithBoth.subjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord3P1WithBoth.subjectId)) assertThat(loaded).hasSize(1) val createdSubject = loaded[0] - assertThat(createdSubject.subjectId).isEqualTo(subject3P1WithBoth.subjectId) - assertThat(createdSubject.samples).hasSize(2) - assertThat(createdSubject.samples).containsExactly(faceSample2, fingerprintSample2) + assertThat(createdSubject.subjectId).isEqualTo(enrolmentRecord3P1WithBoth.subjectId) + assertThat(createdSubject.references).hasSize(2) + assertThat(createdSubject.references).containsExactly(faceSample2, fingerprintSample2) } @Test(expected = IllegalArgumentException::class) // Reverted to JUnit exception check fun `performActions - Creation - should fail without any samples`() = runTest { // Given - val action = SubjectAction.Creation(subjectInvalidNoSamples) // Subject without samples + val action = EnrolmentRecordAction.Creation(enrolmentRecordInvalidNoSamples) // Subject without samples // When dataSource.performActions(listOf(action), project) // This line will throw @@ -315,40 +343,40 @@ class RoomEnrolmentRecordLocalDataSourceTest { fun `performActions - Creation - should create multiple valid subjects`() = runTest { // Given val actions = listOf( - SubjectAction.Creation(subject1P1WithFace), - SubjectAction.Creation(subject2P1WithFinger), - SubjectAction.Creation(subject4P2WithBoth), // Belongs to Project 2 + EnrolmentRecordAction.Creation(enrolmentRecord1P1WithFace), + EnrolmentRecordAction.Creation(enrolmentRecord2P1WithFinger), + EnrolmentRecordAction.Creation(enrolmentRecord4P2WithBoth), // Belongs to Project 2 ) // When dataSource.performActions(actions, project) // Then - val loadedAllP1 = dataSource.load(SubjectQuery(projectId = PROJECT_1_ID, sort = true)) - val loadedAllP2 = dataSource.load(SubjectQuery(projectId = PROJECT_2_ID, sort = true)) + val loadedAllP1 = dataSource.load(EnrolmentRecordQuery(projectId = PROJECT_1_ID, sort = true)) + val loadedAllP2 = dataSource.load(EnrolmentRecordQuery(projectId = PROJECT_2_ID, sort = true)) assertThat(loadedAllP1).hasSize(2) assertThat(loadedAllP1.map { it.subjectId }) .containsExactly( - subject1P1WithFace.subjectId, - subject2P1WithFinger.subjectId, + enrolmentRecord1P1WithFace.subjectId, + enrolmentRecord2P1WithFinger.subjectId, ).inOrder() assertThat(loadedAllP2).hasSize(1) - assertThat(loadedAllP2[0].subjectId).isEqualTo(subject4P2WithBoth.subjectId) + assertThat(loadedAllP2[0].subjectId).isEqualTo(enrolmentRecord4P2WithBoth.subjectId) } @Test fun `performActions - Update - should add face and fingerprint samples`() = runTest { // Given: Create initial subject - dataSource.performActions(listOf(SubjectAction.Creation(subject1P1WithFace)), project) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(enrolmentRecord1P1WithFace)), project) val initialSubject = - dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)).first() - assertThat(initialSubject.samples).hasSize(1) + dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)).first() + assertThat(initialSubject.references).hasSize(1) // Original Update action instantiation style maintained - val updateAction = SubjectAction.Update( - subjectId = subject1P1WithFace.subjectId, + val updateAction = EnrolmentRecordAction.Update( + subjectId = enrolmentRecord1P1WithFace.subjectId, samplesToAdd = listOf(fingerprintSample1, faceSample2), referenceIdsToRemove = listOf(), externalCredentialsToAdd = listOf(), @@ -359,24 +387,24 @@ class RoomEnrolmentRecordLocalDataSourceTest { dataSource.performActions(listOf(updateAction), project) // Then - val loaded = dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)) assertThat(loaded).hasSize(1) val updatedSubject = loaded[0] - assertThat(updatedSubject.samples).hasSize(3) - assertThat(updatedSubject.samples).containsExactly(faceSample1, faceSample2, fingerprintSample1) + assertThat(updatedSubject.references).hasSize(3) + assertThat(updatedSubject.references).containsExactly(faceSample1, faceSample2, fingerprintSample1) } @Test fun `performActions - Update - should remove face sample when fingerprint sample exists`() = runTest { // Given - val subjectToUpdate = subject3P1WithBoth - dataSource.performActions(listOf(SubjectAction.Creation(subjectToUpdate)), project) + val subjectToUpdate = enrolmentRecord3P1WithBoth + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(subjectToUpdate)), project) val initial = - dataSource.load(SubjectQuery(subjectId = subjectToUpdate.subjectId)).first() - assertThat(initial.samples).hasSize(2) + dataSource.load(EnrolmentRecordQuery(subjectId = subjectToUpdate.subjectId)).first() + assertThat(initial.references).hasSize(2) // Original Update action instantiation style maintained - val updateAction = SubjectAction.Update( + val updateAction = EnrolmentRecordAction.Update( subjectId = subjectToUpdate.subjectId, samplesToAdd = listOf(), // Explicitly empty as in original referenceIdsToRemove = listOf(faceSample2.referenceId), @@ -389,22 +417,22 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then val loaded = - dataSource.load(SubjectQuery(subjectId = subjectToUpdate.subjectId)).first() - assertThat(loaded.samples).hasSize(1) - assertThat(loaded.samples).containsExactly(fingerprintSample2) + dataSource.load(EnrolmentRecordQuery(subjectId = subjectToUpdate.subjectId)).first() + assertThat(loaded.references).hasSize(1) + assertThat(loaded.references).containsExactly(fingerprintSample2) } @Test fun `performActions - Update - should remove fingerprint sample when face sample exists`() = runTest { // Given - val subjectToUpdate = subject3P1WithBoth - dataSource.performActions(listOf(SubjectAction.Creation(subjectToUpdate)), project) + val subjectToUpdate = enrolmentRecord3P1WithBoth + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(subjectToUpdate)), project) val initial = - dataSource.load(SubjectQuery(subjectId = subjectToUpdate.subjectId)).first() - assertThat(initial.samples).hasSize(2) + dataSource.load(EnrolmentRecordQuery(subjectId = subjectToUpdate.subjectId)).first() + assertThat(initial.references).hasSize(2) // Original Update action instantiation style maintained - val updateAction = SubjectAction.Update( + val updateAction = EnrolmentRecordAction.Update( subjectId = subjectToUpdate.subjectId, samplesToAdd = listOf(), referenceIdsToRemove = listOf(fingerprintSample2.referenceId), @@ -417,22 +445,22 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then val loaded = - dataSource.load(SubjectQuery(subjectId = subjectToUpdate.subjectId)).first() - assertThat(loaded.samples).hasSize(1) - assertThat(loaded.samples).containsExactly(faceSample2) + dataSource.load(EnrolmentRecordQuery(subjectId = subjectToUpdate.subjectId)).first() + assertThat(loaded.references).hasSize(1) + assertThat(loaded.references).containsExactly(faceSample2) } @Test(expected = IllegalArgumentException::class) // Reverted to JUnit exception check fun `performActions - Update - should fail when removing last face sample`() = runTest { // Given: Subject with only a face sample - dataSource.performActions(listOf(SubjectAction.Creation(subject1P1WithFace)), project) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(enrolmentRecord1P1WithFace)), project) val initial = - dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)).first() - assertThat(initial.samples).hasSize(1) + dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)).first() + assertThat(initial.references).hasSize(1) // Original Update action instantiation style maintained - val updateAction = SubjectAction.Update( - subjectId = subject1P1WithFace.subjectId, + val updateAction = EnrolmentRecordAction.Update( + subjectId = enrolmentRecord1P1WithFace.subjectId, samplesToAdd = listOf(), referenceIdsToRemove = listOf(faceSample1.referenceId), externalCredentialsToAdd = listOf(), @@ -448,13 +476,13 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test(expected = IllegalArgumentException::class) // Reverted to JUnit exception check fun `performActions - Update - should fail when removing last fingerprint sample`() = runTest { // Given: Subject with only a fingerprint sample - dataSource.performActions(listOf(SubjectAction.Creation(subject2P1WithFinger)), project) - val initial = dataSource.load(SubjectQuery(subjectId = subject2P1WithFinger.subjectId)).first() - assertThat(initial.samples).hasSize(1) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(enrolmentRecord2P1WithFinger)), project) + val initial = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord2P1WithFinger.subjectId)).first() + assertThat(initial.references).hasSize(1) // Original Update action instantiation style maintained - val updateAction = SubjectAction.Update( - subjectId = subject2P1WithFinger.subjectId, + val updateAction = EnrolmentRecordAction.Update( + subjectId = enrolmentRecord2P1WithFinger.subjectId, samplesToAdd = listOf(), referenceIdsToRemove = listOf(fingerprintSample1.referenceId), externalCredentialsToAdd = listOf(), @@ -470,11 +498,11 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `performActions - Update - should not add duplicate samples based on ID`() = runTest { // Given: Subject already has faceSample1 - dataSource.performActions(listOf(SubjectAction.Creation(subject1P1WithFace)), project) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(enrolmentRecord1P1WithFace)), project) // Original Update action instantiation style maintained - val updateAction = SubjectAction.Update( - subjectId = subject1P1WithFace.subjectId, + val updateAction = EnrolmentRecordAction.Update( + subjectId = enrolmentRecord1P1WithFace.subjectId, samplesToAdd = listOf(faceSample1, faceSample2, fingerprintSample1), referenceIdsToRemove = listOf(), externalCredentialsToAdd = listOf(), @@ -485,12 +513,12 @@ class RoomEnrolmentRecordLocalDataSourceTest { dataSource.performActions(listOf(updateAction), project) // Then - val loaded = dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)) assertThat(loaded).hasSize(1) val finalSubject = loaded[0] - assertThat(finalSubject.samples).hasSize(3) - assertThat(finalSubject.samples).containsExactly(faceSample1, faceSample2, fingerprintSample1) + assertThat(finalSubject.references).hasSize(3) + assertThat(finalSubject.references).containsExactly(faceSample1, faceSample2, fingerprintSample1) } @Test @@ -500,7 +528,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { val initialCount = dataSource.count() val nonExistentSubjectId = "subj-does-not-exist" - val updateAction = SubjectAction.Update( + val updateAction = EnrolmentRecordAction.Update( subjectId = nonExistentSubjectId, samplesToAdd = listOf(faceSample1), // Try to add samples referenceIdsToRemove = listOf(), @@ -517,7 +545,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { assertThat(finalCount).isEqualTo(initialCount) // Count should not change // Verify the subject was not created - val loaded = dataSource.load(SubjectQuery(subjectId = nonExistentSubjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = nonExistentSubjectId)) assertThat(loaded).isEmpty() } @@ -527,7 +555,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // When - val loaded = dataSource.load(SubjectQuery()) + val loaded = dataSource.load(EnrolmentRecordQuery()) // Then assertThat(loaded).hasSize(initialData.size) @@ -539,12 +567,12 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // When - val loaded = dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)) // Then assertThat(loaded).hasSize(1) - assertThat(loaded[0].subjectId).isEqualTo(subject1P1WithFace.subjectId) - assertThat(loaded[0].samples).containsExactly(faceSample1) + assertThat(loaded[0].subjectId).isEqualTo(enrolmentRecord1P1WithFace.subjectId) + assertThat(loaded[0].references).containsExactly(faceSample1) } @Test @@ -553,7 +581,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // When - val loaded = dataSource.load(SubjectQuery(hasUntokenizedFields = true)) + val loaded = dataSource.load(EnrolmentRecordQuery(hasUntokenizedFields = true)) // Then assertThat(loaded).isEmpty() @@ -565,16 +593,16 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // When - val loadedP1 = dataSource.load(SubjectQuery(projectId = PROJECT_1_ID)) - val loadedP2 = dataSource.load(SubjectQuery(projectId = PROJECT_2_ID)) + val loadedP1 = dataSource.load(EnrolmentRecordQuery(projectId = PROJECT_1_ID)) + val loadedP2 = dataSource.load(EnrolmentRecordQuery(projectId = PROJECT_2_ID)) // Then assertThat(loadedP1).hasSize(3) val loadedP1Ids = loadedP1.map { it.subjectId } assertThat(loadedP1Ids).containsExactly( - subject1P1WithFace.subjectId, - subject2P1WithFinger.subjectId, - subject3P1WithBoth.subjectId, + enrolmentRecord1P1WithFace.subjectId, + enrolmentRecord2P1WithFinger.subjectId, + enrolmentRecord3P1WithBoth.subjectId, ) assertThat(loadedP2).hasSize(3) } @@ -583,15 +611,15 @@ class RoomEnrolmentRecordLocalDataSourceTest { fun `performActions - Deletion - should delete existing subject`() = runTest { // Given setupInitialData() - assertThat(dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId))).isNotEmpty() + assertThat(dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId))).isNotEmpty() - val deleteAction = SubjectAction.Deletion(subjectId = subject1P1WithFace.subjectId) + val deleteAction = EnrolmentRecordAction.Deletion(subjectId = enrolmentRecord1P1WithFace.subjectId) // When dataSource.performActions(listOf(deleteAction), project) // Then - val loaded = dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)) + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)) assertThat(loaded).isEmpty() assertThat(dataSource.count()).isEqualTo(initialData.size - 1) } @@ -599,16 +627,16 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `combined actions - create, update, delete`() = runTest { // --- Create --- - val createAction = SubjectAction.Creation(subject1P1WithFace) + val createAction = EnrolmentRecordAction.Creation(enrolmentRecord1P1WithFace) dataSource.performActions(listOf(createAction), project) var loadedSubject = - dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)).firstOrNull() + dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)).firstOrNull() assertThat(loadedSubject).isNotNull() - assertThat(loadedSubject!!.samples).hasSize(1) + assertThat(loadedSubject!!.references).hasSize(1) // --- Update (Original style maintained) --- - val updateAction = SubjectAction.Update( - subjectId = subject1P1WithFace.subjectId, + val updateAction = EnrolmentRecordAction.Update( + subjectId = enrolmentRecord1P1WithFace.subjectId, samplesToAdd = listOf(fingerprintSample1), referenceIdsToRemove = listOf(), externalCredentialsToAdd = listOf(), @@ -616,15 +644,15 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) dataSource.performActions(listOf(updateAction), project) loadedSubject = - dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)).firstOrNull() + dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)).firstOrNull() assertThat(loadedSubject).isNotNull() - assertThat(loadedSubject!!.samples).hasSize(2) - assertThat(loadedSubject.samples).containsExactly(fingerprintSample1, faceSample1) + assertThat(loadedSubject!!.references).hasSize(2) + assertThat(loadedSubject.references).containsExactly(fingerprintSample1, faceSample1) // --- Delete --- - val deleteAction = SubjectAction.Deletion(subjectId = subject1P1WithFace.subjectId) + val deleteAction = EnrolmentRecordAction.Deletion(subjectId = enrolmentRecord1P1WithFace.subjectId) dataSource.performActions(listOf(deleteAction), project) - val finalLoad = dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId)) + val finalLoad = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId)) assertThat(finalLoad).isEmpty() } @@ -646,8 +674,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // When - val countP1 = dataSource.count(SubjectQuery(projectId = PROJECT_1_ID)) - val countP2 = dataSource.count(SubjectQuery(projectId = PROJECT_2_ID)) + val countP1 = dataSource.count(EnrolmentRecordQuery(projectId = PROJECT_1_ID)) + val countP2 = dataSource.count(EnrolmentRecordQuery(projectId = PROJECT_2_ID)) // Then assertThat(countP1).isEqualTo(3) @@ -660,8 +688,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // When - val countAtt1 = dataSource.count(SubjectQuery(attendantId = ATTENDANT_1_ID)) - val countAtt2 = dataSource.count(SubjectQuery(attendantId = ATTENDANT_2_ID)) + val countAtt1 = dataSource.count(EnrolmentRecordQuery(attendantId = ATTENDANT_1_ID)) + val countAtt2 = dataSource.count(EnrolmentRecordQuery(attendantId = ATTENDANT_2_ID)) // Then assertThat(countAtt1).isEqualTo(4) @@ -674,8 +702,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // When - val countModule1 = dataSource.count(SubjectQuery(moduleId = MODULE_1_ID)) - val countModule2 = dataSource.count(SubjectQuery(moduleId = MODULE_2_ID)) + val countModule1 = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_1_ID)) + val countModule2 = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_2_ID)) // Then assertThat(countModule1).isEqualTo(2) @@ -689,24 +717,24 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val countRoc1P1 = dataSource.count( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = ROC_1_FORMAT, ), ) val countRoc3P1 = dataSource.count( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = ROC_3_FORMAT, ), ) val countRoc1P2 = dataSource.count( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_2_ID, format = ROC_1_FORMAT, ), ) - val countAllRoc1 = dataSource.count(SubjectQuery(format = ROC_1_FORMAT)) + val countAllRoc1 = dataSource.count(EnrolmentRecordQuery(format = ROC_1_FORMAT)) // Then assertThat(countRoc1P1).isEqualTo(1) @@ -722,24 +750,24 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val countNecP1 = dataSource.count( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = NEC_FORMAT, ), ) val countIsoP1 = dataSource.count( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = ISO_FORMAT, ), ) val countNecP2 = dataSource.count( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_2_ID, format = NEC_FORMAT, ), ) - val countAllNec = dataSource.count(SubjectQuery(format = NEC_FORMAT)) + val countAllNec = dataSource.count(EnrolmentRecordQuery(format = NEC_FORMAT)) // Then assertThat(countNecP1).isEqualTo(1) @@ -755,7 +783,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val count = dataSource.count( - SubjectQuery( + EnrolmentRecordQuery( projectId = "non-existent-project", moduleId = "non-existent-module".asTokenizableEncrypted(), attendantId = "non-existent-attendant".asTokenizableEncrypted(), @@ -770,10 +798,10 @@ class RoomEnrolmentRecordLocalDataSourceTest { fun `loadIdentities - should throw exception if format is missing in query`() = runTest { // Given setupInitialData() - val queryWithoutFormat = SubjectQuery(projectId = PROJECT_1_ID) + val queryWithoutFormat = EnrolmentRecordQuery(projectId = PROJECT_1_ID) // When - dataSource.loadIdentities( + dataSource.loadCandidateRecords( // This call will throw query = queryWithoutFormat, ranges = listOf(0..10), @@ -796,8 +824,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When - Query P1 for NEC val loadedP1Nec = dataSource - .loadIdentities( - query = SubjectQuery( + .loadCandidateRecords( + query = EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = NEC_FORMAT, ), @@ -811,8 +839,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { .identities // When - Query P1 for ISO val loadedP1Iso = dataSource - .loadIdentities( - query = SubjectQuery( + .loadCandidateRecords( + query = EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = ISO_FORMAT, ), @@ -826,8 +854,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { .identities // When - Query P2 for NEC val loadedP2Nec = dataSource - .loadIdentities( - query = SubjectQuery( + .loadCandidateRecords( + query = EnrolmentRecordQuery( projectId = PROJECT_2_ID, format = NEC_FORMAT, ), @@ -842,23 +870,25 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then - P1 NEC assertThat(loadedP1Nec).hasSize(1) - assertThat(loadedP1Nec[0].subjectId).isEqualTo(subject2P1WithFinger.subjectId) - assertThat(loadedP1Nec[0].samples).hasSize(1) - assertThat(loadedP1Nec[0].samples[0].format).isEqualTo(NEC_FORMAT) - assertThat(loadedP1Nec[0].samples).isEqualTo(subject2P1WithFinger.samples) + assertThat(loadedP1Nec[0].subjectId).isEqualTo(enrolmentRecord2P1WithFinger.subjectId) + assertThat(loadedP1Nec[0].references).hasSize(1) + assertThat(loadedP1Nec[0].references[0].format).isEqualTo(NEC_FORMAT) + assertThat(loadedP1Nec[0].references).isEqualTo(enrolmentRecord2P1WithFinger.references) // Then - P1 ISO assertThat(loadedP1Iso).hasSize(1) - assertThat(loadedP1Iso[0].subjectId).isEqualTo(subject3P1WithBoth.subjectId) - assertThat(loadedP1Iso[0].samples).hasSize(1) - assertThat(loadedP1Iso[0].samples[0].format).isEqualTo(ISO_FORMAT) - assertThat(loadedP1Iso[0].samples).isEqualTo(subject3P1WithBoth.samples.filter { it.modality == Modality.FINGERPRINT }) + assertThat(loadedP1Iso[0].subjectId).isEqualTo(enrolmentRecord3P1WithBoth.subjectId) + assertThat(loadedP1Iso[0].references).hasSize(1) + assertThat(loadedP1Iso[0].references[0].format).isEqualTo(ISO_FORMAT) + assertThat( + loadedP1Iso[0].references, + ).isEqualTo(enrolmentRecord3P1WithBoth.references.filter { it.modality == Modality.FINGERPRINT }) // Then - P2 NEC assertThat(loadedP2Nec).hasSize(2) - assertThat(loadedP2Nec[0].subjectId).isEqualTo(subject4P2WithBoth.subjectId) - assertThat(loadedP2Nec[0].samples).hasSize(1) - assertThat(loadedP2Nec[0].samples[0].format).isEqualTo(NEC_FORMAT) + assertThat(loadedP2Nec[0].subjectId).isEqualTo(enrolmentRecord4P2WithBoth.subjectId) + assertThat(loadedP2Nec[0].references).hasSize(1) + assertThat(loadedP2Nec[0].references[0].format).isEqualTo(NEC_FORMAT) verify(exactly = 4) { mockCallback() } } @@ -866,42 +896,32 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `loadIdentities - should respect the range parameter for specific fingerprint format`() = runTest { // Given: More data - val subject5P1WithNec = subject2P1WithFinger.copy( + val subject5P1WithNec = enrolmentRecord2P1WithFinger.copy( subjectId = "subj-005", - samples = listOf( - fingerprintSample1.copy( - id = "fp-uuid-5", - referenceId = "ref-fp-5", - ), - ), + references = listOf(fingerprintSample1.copyWithTemplateId("fp-uuid-5").copy(referenceId = "ref-fp-5")), createdAt = Date(date.time + 1000), ) - val subject6P1WithNec = subject2P1WithFinger.copy( + val subject6P1WithNec = enrolmentRecord2P1WithFinger.copy( subjectId = "subj-006", - samples = listOf( - fingerprintSample1.copy( - id = "fp-uuid-6", - referenceId = "ref-fp-6", - ), - ), + references = listOf(fingerprintSample1.copyWithTemplateId("fp-uuid-6").copy(referenceId = "ref-fp-6")), createdAt = Date(date.time + 2000), ) setupInitialData() dataSource.performActions( listOf( - SubjectAction.Creation(subject5P1WithNec), - SubjectAction.Creation(subject6P1WithNec), + EnrolmentRecordAction.Creation(subject5P1WithNec), + EnrolmentRecordAction.Creation(subject6P1WithNec), ), project, ) - val baseQuery = SubjectQuery( + val baseQuery = EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = NEC_FORMAT, ) // When val loadedRanges = dataSource - .loadIdentities( + .loadCandidateRecords( query = baseQuery, ranges = listOf( 0..0, @@ -915,7 +935,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { val loadedFirstTwo = dataSource - .loadIdentities( + .loadCandidateRecords( query = baseQuery, ranges = listOf( 0..1, @@ -929,7 +949,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { .identities val loadedAll = dataSource - .loadIdentities( + .loadCandidateRecords( query = baseQuery, ranges = listOf(0..10), project = project, @@ -942,17 +962,17 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then assertThat(loadedRanges).hasSize(2) - assertThat(loadedRanges[0].identities[0].subjectId).isEqualTo(subject2P1WithFinger.subjectId) + assertThat(loadedRanges[0].identities[0].subjectId).isEqualTo(enrolmentRecord2P1WithFinger.subjectId) assertThat(loadedRanges[1].identities).hasSize(1) assertThat(loadedRanges[1].identities[0].subjectId).isEqualTo(subject5P1WithNec.subjectId) assertThat(loadedFirstTwo).hasSize(2) assertThat(loadedFirstTwo.map { it.subjectId }) - .containsExactly(subject2P1WithFinger.subjectId, subject5P1WithNec.subjectId) + .containsExactly(enrolmentRecord2P1WithFinger.subjectId, subject5P1WithNec.subjectId) .inOrder() assertThat(loadedAll).hasSize(3) assertThat(loadedAll.map { it.subjectId }) .containsExactly( - subject2P1WithFinger.subjectId, + enrolmentRecord2P1WithFinger.subjectId, subject5P1WithNec.subjectId, subject6P1WithNec.subjectId, ).inOrder() @@ -966,8 +986,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val loadedIdentities = dataSource - .loadIdentities( - query = SubjectQuery( + .loadCandidateRecords( + query = EnrolmentRecordQuery( projectId = PROJECT_1_ID, format = UNUSED_FORMAT, ), @@ -992,8 +1012,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val loadedP1Roc1 = dataSource - .loadIdentities( - query = SubjectQuery(projectId = PROJECT_1_ID, format = ROC_1_FORMAT), + .loadCandidateRecords( + query = EnrolmentRecordQuery(projectId = PROJECT_1_ID, format = ROC_1_FORMAT), ranges = listOf(0..10), project = project, dataSource = Simprints, @@ -1003,8 +1023,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { .first() .identities val loadedP1Roc3 = dataSource - .loadIdentities( - query = SubjectQuery(projectId = PROJECT_1_ID, format = ROC_3_FORMAT), + .loadCandidateRecords( + query = EnrolmentRecordQuery(projectId = PROJECT_1_ID, format = ROC_3_FORMAT), ranges = listOf(0..10), project = project, dataSource = Simprints, @@ -1014,8 +1034,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { .first() .identities val loadedP2Roc1 = dataSource - .loadIdentities( - query = SubjectQuery(projectId = PROJECT_2_ID, format = ROC_1_FORMAT), + .loadCandidateRecords( + query = EnrolmentRecordQuery(projectId = PROJECT_2_ID, format = ROC_1_FORMAT), ranges = listOf(0..10), project = project, dataSource = Simprints, @@ -1027,21 +1047,21 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then - P1 ROC_1 assertThat(loadedP1Roc1).hasSize(1) - assertThat(loadedP1Roc1[0].subjectId).isEqualTo(subject1P1WithFace.subjectId) - assertThat(loadedP1Roc1[0].samples).hasSize(1) - assertThat(loadedP1Roc1[0].samples[0].format).isEqualTo(ROC_1_FORMAT) - assertThat(loadedP1Roc1[0].samples).isEqualTo(subject1P1WithFace.samples) + assertThat(loadedP1Roc1[0].subjectId).isEqualTo(enrolmentRecord1P1WithFace.subjectId) + assertThat(loadedP1Roc1[0].references).hasSize(1) + assertThat(loadedP1Roc1[0].references[0].format).isEqualTo(ROC_1_FORMAT) + assertThat(loadedP1Roc1[0].references).isEqualTo(enrolmentRecord1P1WithFace.references) // Then - P1 ROC_3 assertThat(loadedP1Roc3).hasSize(1) - assertThat(loadedP1Roc3[0].subjectId).isEqualTo(subject3P1WithBoth.subjectId) - assertThat(loadedP1Roc3[0].samples).hasSize(1) - assertThat(loadedP1Roc3[0].samples[0].format).isEqualTo(ROC_3_FORMAT) - assertThat(loadedP1Roc3[0].samples).isEqualTo(subject3P1WithBoth.samples.filter { it.modality == Modality.FACE }) + assertThat(loadedP1Roc3[0].subjectId).isEqualTo(enrolmentRecord3P1WithBoth.subjectId) + assertThat(loadedP1Roc3[0].references).hasSize(1) + assertThat(loadedP1Roc3[0].references[0].format).isEqualTo(ROC_3_FORMAT) + assertThat(loadedP1Roc3[0].references).isEqualTo(enrolmentRecord3P1WithBoth.references.filter { it.modality == Modality.FACE }) // Then - P2 ROC_1 assertThat(loadedP2Roc1).hasSize(2) - assertThat(loadedP2Roc1[0].subjectId).isEqualTo(subject4P2WithBoth.subjectId) + assertThat(loadedP2Roc1[0].subjectId).isEqualTo(enrolmentRecord4P2WithBoth.subjectId) verify(exactly = 4) { mockCallback() } } @@ -1049,30 +1069,30 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `loadIdentities - should respect the range parameter for specific face format`() = runTest { // Given: More data - val subject5P1WithRoc1 = subject1P1WithFace.copy( + val subject5P1WithRoc1 = enrolmentRecord1P1WithFace.copy( subjectId = "subj-005", - samples = listOf(faceSample1.copy(id = "face-uuid-5", referenceId = "ref-face-5")), + references = listOf(faceSample1.copyWithTemplateId("face-uuid-5").copy(referenceId = "ref-face-5")), createdAt = Date(date.time + 1000), ) - val subject6P1WithRoc1 = subject1P1WithFace.copy( + val subject6P1WithRoc1 = enrolmentRecord1P1WithFace.copy( subjectId = "subj-006", - samples = listOf(faceSample1.copy(id = "face-uuid-6", referenceId = "ref-face-6")), + references = listOf(faceSample1.copyWithTemplateId("face-uuid-6").copy(referenceId = "ref-face-6")), createdAt = Date(date.time + 2000), ) setupInitialData() dataSource.performActions( listOf( - SubjectAction.Creation(subject5P1WithRoc1), - SubjectAction.Creation(subject6P1WithRoc1), + EnrolmentRecordAction.Creation(subject5P1WithRoc1), + EnrolmentRecordAction.Creation(subject6P1WithRoc1), ), project, ) val baseQuery = - SubjectQuery(projectId = PROJECT_1_ID, format = ROC_1_FORMAT, sort = true) + EnrolmentRecordQuery(projectId = PROJECT_1_ID, format = ROC_1_FORMAT, sort = true) // When val loadedRanges = dataSource - .loadIdentities( + .loadCandidateRecords( query = baseQuery, ranges = listOf( 0..0, @@ -1086,7 +1106,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { val loadedFirstTwo = dataSource - .loadIdentities( + .loadCandidateRecords( query = baseQuery, ranges = listOf( 0..1, @@ -1099,7 +1119,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { .first() .identities val loadedAll = dataSource - .loadIdentities( + .loadCandidateRecords( query = baseQuery, ranges = listOf(0..10), project = project, @@ -1112,17 +1132,17 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then assertThat(loadedRanges).hasSize(2) // Two ranges loaded - assertThat(loadedRanges[0].identities[0].subjectId).isEqualTo(subject1P1WithFace.subjectId) + assertThat(loadedRanges[0].identities[0].subjectId).isEqualTo(enrolmentRecord1P1WithFace.subjectId) assertThat(loadedRanges[1].identities).hasSize(1) assertThat(loadedRanges[1].identities[0].subjectId).isEqualTo(subject5P1WithRoc1.subjectId) assertThat(loadedFirstTwo).hasSize(2) assertThat(loadedFirstTwo.map { it.subjectId }) - .containsExactly(subject1P1WithFace.subjectId, subject5P1WithRoc1.subjectId) + .containsExactly(enrolmentRecord1P1WithFace.subjectId, subject5P1WithRoc1.subjectId) .inOrder() assertThat(loadedAll).hasSize(3) assertThat(loadedAll.map { it.subjectId }) .containsExactly( - subject1P1WithFace.subjectId, + enrolmentRecord1P1WithFace.subjectId, subject5P1WithRoc1.subjectId, subject6P1WithRoc1.subjectId, ).inOrder() @@ -1135,7 +1155,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // Query for Project 1, Attendant 1, Module 1, Format ROC_1 - val queryP1A1M1Roc1 = SubjectQuery( + val queryP1A1M1Roc1 = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_1_ID, @@ -1143,7 +1163,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) // Query for Project 1, Attendant 1, Module 2, Format ROC_3 - val queryP1A1M2Roc3 = SubjectQuery( + val queryP1A1M2Roc3 = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_2_ID, @@ -1151,7 +1171,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) // Query for Project 1, Attendant 1, Module 1, Format ROC_3 (should be empty) - val queryP1A1M1Roc3Empty = SubjectQuery( + val queryP1A1M1Roc3Empty = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_1_ID, @@ -1160,7 +1180,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val loadedP1A1M1Roc1 = dataSource - .loadIdentities( + .loadCandidateRecords( queryP1A1M1Roc1, listOf(0..10), project = project, @@ -1171,7 +1191,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { .first() .identities val loadedP1A1M2Roc3 = dataSource - .loadIdentities( + .loadCandidateRecords( queryP1A1M2Roc3, listOf( 0..10, @@ -1185,7 +1205,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { .identities val loadedP1A1M1Roc3Empty = dataSource - .loadIdentities( + .loadCandidateRecords( queryP1A1M1Roc3Empty, listOf( 0..10, @@ -1200,10 +1220,10 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then assertThat(loadedP1A1M1Roc1).hasSize(1) - assertThat(loadedP1A1M1Roc1[0].subjectId).isEqualTo(subject1P1WithFace.subjectId) + assertThat(loadedP1A1M1Roc1[0].subjectId).isEqualTo(enrolmentRecord1P1WithFace.subjectId) assertThat(loadedP1A1M2Roc3).hasSize(1) - assertThat(loadedP1A1M2Roc3[0].subjectId).isEqualTo(subject3P1WithBoth.subjectId) + assertThat(loadedP1A1M2Roc3[0].subjectId).isEqualTo(enrolmentRecord3P1WithBoth.subjectId) assertThat(loadedP1A1M1Roc3Empty).isEmpty() @@ -1216,7 +1236,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // Query for Project 1, Attendant 1, Module 1, Format NEC - val queryP1A1M1Nec = SubjectQuery( + val queryP1A1M1Nec = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_1_ID, @@ -1224,7 +1244,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) // Query for Project 1, Attendant 1, Module 3, Format NEC - val queryP1A1M3Nec = SubjectQuery( + val queryP1A1M3Nec = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_3_ID, @@ -1232,7 +1252,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) // Query for Project 1, Attendant 1, Module 2, Format ISO - val queryP1A1M2Iso = SubjectQuery( + val queryP1A1M2Iso = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_2_ID, @@ -1240,7 +1260,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) // Query for Project 1, Attendant 2, Module 1, Format NEC (should be empty) - val queryP1A2M1NecEmpty = SubjectQuery( + val queryP1A2M1NecEmpty = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_2_ID, moduleId = MODULE_1_ID, @@ -1249,7 +1269,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val loadedP1A1M1Nec = dataSource - .loadIdentities( + .loadCandidateRecords( queryP1A1M1Nec, listOf( 0..10, @@ -1262,7 +1282,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { .first() .identities val loadedP1A1M3Nec = dataSource - .loadIdentities( + .loadCandidateRecords( queryP1A1M3Nec, listOf( 0..10, @@ -1275,7 +1295,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { .first() .identities val loadedP1A1M2Iso = dataSource - .loadIdentities( + .loadCandidateRecords( queryP1A1M2Iso, listOf( 0..10, @@ -1288,7 +1308,7 @@ class RoomEnrolmentRecordLocalDataSourceTest { .first() .identities val loadedP1A2M1NecEmpty = dataSource - .loadIdentities( + .loadCandidateRecords( queryP1A2M1NecEmpty, listOf( 0..10, @@ -1303,12 +1323,12 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then assertThat(loadedP1A1M1Nec).hasSize(1) - assertThat(loadedP1A1M1Nec[0].subjectId).isEqualTo(subject2P1WithFinger.subjectId) + assertThat(loadedP1A1M1Nec[0].subjectId).isEqualTo(enrolmentRecord2P1WithFinger.subjectId) assertThat(loadedP1A1M3Nec).hasSize(0) assertThat(loadedP1A1M2Iso).hasSize(1) - assertThat(loadedP1A1M2Iso[0].subjectId).isEqualTo(subject3P1WithBoth.subjectId) + assertThat(loadedP1A1M2Iso[0].subjectId).isEqualTo(enrolmentRecord3P1WithBoth.subjectId) assertThat(loadedP1A2M1NecEmpty).isEmpty() @@ -1322,28 +1342,28 @@ class RoomEnrolmentRecordLocalDataSourceTest { // When val loadedP1A1M1 = dataSource.load( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_1_ID, ), ) val loadedP1A1M2 = dataSource.load( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, moduleId = MODULE_2_ID, ), ) val loadedP1A2M1 = dataSource.load( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_2_ID, moduleId = MODULE_1_ID, ), ) val loadedP2A2M2 = dataSource.load( - SubjectQuery( + EnrolmentRecordQuery( projectId = PROJECT_2_ID, attendantId = ATTENDANT_2_ID, moduleId = MODULE_2_ID, @@ -1353,17 +1373,17 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Then assertThat(loadedP1A1M1).hasSize(2) assertThat(loadedP1A1M1.map { it.subjectId }).containsExactly( - subject1P1WithFace.subjectId, - subject2P1WithFinger.subjectId, + enrolmentRecord1P1WithFace.subjectId, + enrolmentRecord2P1WithFinger.subjectId, ) assertThat(loadedP1A1M2).hasSize(1) - assertThat(loadedP1A1M2[0].subjectId).isEqualTo(subject3P1WithBoth.subjectId) + assertThat(loadedP1A1M2[0].subjectId).isEqualTo(enrolmentRecord3P1WithBoth.subjectId) assertThat(loadedP1A2M1).hasSize(0) assertThat(loadedP2A2M2).hasSize(1) - assertThat(loadedP2A2M2[0].subjectId).isEqualTo(subject4P2WithBoth.subjectId) + assertThat(loadedP2A2M2[0].subjectId).isEqualTo(enrolmentRecord4P2WithBoth.subjectId) } @Test @@ -1371,14 +1391,14 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Given setupInitialData() val targetIds = listOf( - subject1P1WithFace.subjectId, - subject4P2WithBoth.subjectId, - subject6P2WithFinger.subjectId, + enrolmentRecord1P1WithFace.subjectId, + enrolmentRecord4P2WithBoth.subjectId, + enrolmentRecord6P2WithFinger.subjectId, ) // When val loaded = dataSource.load( - SubjectQuery( + EnrolmentRecordQuery( subjectIds = targetIds, sort = true, ), @@ -1390,11 +1410,11 @@ class RoomEnrolmentRecordLocalDataSourceTest { .containsExactlyElementsIn(targetIds.sorted()) .inOrder() // Compare sorted lists // Check details of one subject - val loadedSubject1 = loaded.find { it.subjectId == subject1P1WithFace.subjectId } + val loadedSubject1 = loaded.find { it.subjectId == enrolmentRecord1P1WithFace.subjectId } assertThat(loadedSubject1).isNotNull() assertThat(loadedSubject1?.attendantId).isEqualTo(ATTENDANT_1_ID) assertThat(loadedSubject1?.moduleId).isEqualTo(MODULE_1_ID) - assertThat(loadedSubject1?.samples).isEqualTo(subject1P1WithFace.samples) + assertThat(loadedSubject1?.references).isEqualTo(enrolmentRecord1P1WithFace.references) } @Test @@ -1402,19 +1422,19 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Given setupInitialData() val allSubjectIdsSorted = listOf( - subject1P1WithFace.subjectId, // subj-001 - subject2P1WithFinger.subjectId, // subj-002 - subject3P1WithBoth.subjectId, // subj-003 - subject4P2WithBoth.subjectId, // subj-004 - subject5P2WithFace.subjectId, // subj-005 - subject6P2WithFinger.subjectId, // subj-006 + enrolmentRecord1P1WithFace.subjectId, // subj-001 + enrolmentRecord2P1WithFinger.subjectId, // subj-002 + enrolmentRecord3P1WithBoth.subjectId, // subj-003 + enrolmentRecord4P2WithBoth.subjectId, // subj-004 + enrolmentRecord5P2WithFace.subjectId, // subj-005 + enrolmentRecord6P2WithFinger.subjectId, // subj-006 ).sorted() val afterId = allSubjectIdsSorted[2] // Should be subj-003 // When // Query for all subjects after subj-003, sorted by ID - val loaded = dataSource.load(SubjectQuery(afterSubjectId = afterId, sort = true)) + val loaded = dataSource.load(EnrolmentRecordQuery(afterSubjectId = afterId, sort = true)) // Then assertThat(loaded).hasSize(3) // subj-004, subj-005, subj-006 remain @@ -1445,13 +1465,13 @@ class RoomEnrolmentRecordLocalDataSourceTest { setupInitialData() // Targets: subj-001, subj-002 (P1, A1, M1), subj-003 (P1, A1, M2) val targetIds = listOf( - subject2P1WithFinger.subjectId, // subj-002 - subject3P1WithBoth.subjectId, // subj-003 + enrolmentRecord2P1WithFinger.subjectId, // subj-002 + enrolmentRecord3P1WithBoth.subjectId, // subj-003 "subj-nonexistent", // Include a non-existent ID ) // Query: Project 1, Attendant 1, from the targetIds list, sorted - val query = SubjectQuery( + val query = EnrolmentRecordQuery( projectId = PROJECT_1_ID, attendantId = ATTENDANT_1_ID, subjectIds = targetIds, @@ -1466,8 +1486,8 @@ class RoomEnrolmentRecordLocalDataSourceTest { assertThat(loaded).hasSize(2) assertThat(loaded.map { it.subjectId }) .containsExactly( - subject2P1WithFinger.subjectId, // subj-002 - subject3P1WithBoth.subjectId, // subj-003 + enrolmentRecord2P1WithFinger.subjectId, // subj-002 + enrolmentRecord3P1WithBoth.subjectId, // subj-003 ).inOrder() } @@ -1478,24 +1498,24 @@ class RoomEnrolmentRecordLocalDataSourceTest { // Given setupInitialData() val initialCount = dataSource.count() - val countModule1Before = dataSource.count(SubjectQuery(moduleId = MODULE_1_ID)) - val countModule2Before = dataSource.count(SubjectQuery(moduleId = MODULE_2_ID)) - val countModule3Before = dataSource.count(SubjectQuery(moduleId = MODULE_3_ID)) + val countModule1Before = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_1_ID)) + val countModule2Before = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_2_ID)) + val countModule3Before = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_3_ID)) assertThat(countModule1Before).isEqualTo(2) assertThat(countModule2Before).isEqualTo(2) assertThat(countModule3Before).isEqualTo(2) - val queryToDeleteModule1 = SubjectQuery(moduleId = MODULE_1_ID) + val queryToDeleteModule1 = EnrolmentRecordQuery(moduleId = MODULE_1_ID) // When dataSource.delete(listOf(queryToDeleteModule1)) // Then val finalCount = dataSource.count() - val countModule1After = dataSource.count(SubjectQuery(moduleId = MODULE_1_ID)) - val countModule2After = dataSource.count(SubjectQuery(moduleId = MODULE_2_ID)) - val countModule3After = dataSource.count(SubjectQuery(moduleId = MODULE_3_ID)) + val countModule1After = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_1_ID)) + val countModule2After = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_2_ID)) + val countModule3After = dataSource.count(EnrolmentRecordQuery(moduleId = MODULE_3_ID)) assertThat(countModule1After).isEqualTo(0) // All M1 deleted assertThat(countModule2After).isEqualTo(2) // M2 untouched @@ -1503,19 +1523,19 @@ class RoomEnrolmentRecordLocalDataSourceTest { assertThat(finalCount).isEqualTo(initialCount - countModule1Before) // Total count reduced correctly // Verify specific subjects are gone/remain - assertThat(dataSource.load(SubjectQuery(subjectId = subject1P1WithFace.subjectId))).isEmpty() - assertThat(dataSource.load(SubjectQuery(subjectId = subject2P1WithFinger.subjectId))).isEmpty() - assertThat(dataSource.load(SubjectQuery(subjectId = subject5P2WithFace.subjectId))).isNotEmpty() - assertThat(dataSource.load(SubjectQuery(subjectId = subject3P1WithBoth.subjectId))).isNotEmpty() - assertThat(dataSource.load(SubjectQuery(subjectId = subject4P2WithBoth.subjectId))).isNotEmpty() - assertThat(dataSource.load(SubjectQuery(subjectId = subject6P2WithFinger.subjectId))).isNotEmpty() + assertThat(dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord1P1WithFace.subjectId))).isEmpty() + assertThat(dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord2P1WithFinger.subjectId))).isEmpty() + assertThat(dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord5P2WithFace.subjectId))).isNotEmpty() + assertThat(dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord3P1WithBoth.subjectId))).isNotEmpty() + assertThat(dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord4P2WithBoth.subjectId))).isNotEmpty() + assertThat(dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord6P2WithFinger.subjectId))).isNotEmpty() } @Test(expected = IllegalArgumentException::class) fun `delete - by moduleId with format specified - should throw exception`() = runTest { // Given setupInitialData() - val queryToDeleteModule1WithFormat = SubjectQuery( + val queryToDeleteModule1WithFormat = EnrolmentRecordQuery( moduleId = MODULE_1_ID, format = ROC_1_FORMAT, // Adding format which is not allowed for delete ) @@ -1566,20 +1586,20 @@ class RoomEnrolmentRecordLocalDataSourceTest { @Test fun `performActions - Update - should succeed when removing all samples but adding external credentials`() = runTest { - dataSource.performActions(listOf(SubjectAction.Creation(subject3P1WithBoth)), project) - val initial = dataSource.load(SubjectQuery(subjectId = subject3P1WithBoth.subjectId)).first() - assertThat(initial.samples).hasSize(2) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(enrolmentRecord3P1WithBoth)), project) + val initial = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord3P1WithBoth.subjectId)).first() + assertThat(initial.references).hasSize(2) assertThat(initial.externalCredentials).hasSize(1) val newExternalCredential = ExternalCredential( id = "new-credential-id", value = "new-value".asTokenizableEncrypted(), - subjectId = subject3P1WithBoth.subjectId, + subjectId = enrolmentRecord3P1WithBoth.subjectId, type = ExternalCredentialType.NHISCard, ) - val updateAction = SubjectAction.Update( - subjectId = subject3P1WithBoth.subjectId, + val updateAction = EnrolmentRecordAction.Update( + subjectId = enrolmentRecord3P1WithBoth.subjectId, samplesToAdd = listOf(), referenceIdsToRemove = listOf(faceSample2.referenceId, fingerprintSample2.referenceId), // Remove all samples externalCredentialsToAdd = listOf(newExternalCredential), @@ -1587,34 +1607,34 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) dataSource.performActions(listOf(updateAction), project) - val loaded = dataSource.load(SubjectQuery(subjectId = subject3P1WithBoth.subjectId)).first() - assertThat(loaded.samples).isEmpty() + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord3P1WithBoth.subjectId)).first() + assertThat(loaded.references).isEmpty() assertThat(loaded.externalCredentials).hasSize(2) assertThat(loaded.externalCredentials).contains(newExternalCredential) } @Test fun `performActions - Update - should succeed when removing external credentials`() = runTest { - val subject = subject3P1WithBoth.copy( + val subject = enrolmentRecord3P1WithBoth.copy( externalCredentials = listOf( ExternalCredential( id = "credential-id-1", value = "value-1".asTokenizableEncrypted(), - subjectId = subject3P1WithBoth.subjectId, + subjectId = enrolmentRecord3P1WithBoth.subjectId, type = ExternalCredentialType.NHISCard, ), ExternalCredential( id = "credential-id-2", value = "value-2".asTokenizableEncrypted(), - subjectId = subject3P1WithBoth.subjectId, + subjectId = enrolmentRecord3P1WithBoth.subjectId, type = ExternalCredentialType.NHISCard, ), ), ) - dataSource.performActions(listOf(SubjectAction.Creation(subject)), project) + dataSource.performActions(listOf(EnrolmentRecordAction.Creation(subject)), project) - val updateAction = SubjectAction.Update( - subjectId = subject3P1WithBoth.subjectId, + val updateAction = EnrolmentRecordAction.Update( + subjectId = enrolmentRecord3P1WithBoth.subjectId, samplesToAdd = listOf(), referenceIdsToRemove = listOf(), externalCredentialsToAdd = listOf(), @@ -1622,8 +1642,12 @@ class RoomEnrolmentRecordLocalDataSourceTest { ) dataSource.performActions(listOf(updateAction), project) - val loaded = dataSource.load(SubjectQuery(subjectId = subject3P1WithBoth.subjectId)).first() + val loaded = dataSource.load(EnrolmentRecordQuery(subjectId = enrolmentRecord3P1WithBoth.subjectId)).first() assertThat(loaded.externalCredentials).hasSize(1) assertThat(loaded.externalCredentials.map { it.id }).containsExactly("credential-id-2") } + + private fun BiometricReference.copyWithTemplateId(newId: String) = copy( + templates = templates.map { it.copy(id = newId) }, + ) } diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilderTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilderTest.kt index 3c71b11bb9..8196a426c2 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilderTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/RoomEnrolmentRecordQueryBuilderTest.kt @@ -4,7 +4,7 @@ import androidx.sqlite.db.SimpleSQLiteQuery import androidx.sqlite.db.SupportSQLiteProgram import com.google.common.truth.Truth.* import com.simprints.core.domain.tokenization.asTokenizableEncrypted -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.enrolment.records.room.store.models.DbBiometricTemplate.Companion.FORMAT_COLUMN import com.simprints.infra.enrolment.records.room.store.models.DbBiometricTemplate.Companion.TEMPLATE_TABLE_NAME import com.simprints.infra.enrolment.records.room.store.models.DbExternalCredential.Companion.EXTERNAL_CREDENTIAL_TABLE_NAME @@ -33,10 +33,10 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with empty query returns select all`() { - val subjectQuery = SubjectQuery() + val enrolmentRecordQuery = EnrolmentRecordQuery() val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql) assertThat(resultQuery.argCount).isEqualTo(0) @@ -45,11 +45,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with subjectId`() { val subjectId = "test-subject-id" - val subjectQuery = SubjectQuery(subjectId = subjectId) + val enrolmentRecordQuery = EnrolmentRecordQuery(subjectId = subjectId) val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S\n\n" + "WHERE S.$SUBJECT_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(subjectId)) @@ -58,11 +58,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with subjectIds`() { val subjectIds = listOf("id1", "id2", "id3") - val subjectQuery = SubjectQuery(subjectIds = subjectIds) + val enrolmentRecordQuery = EnrolmentRecordQuery(subjectIds = subjectIds) val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S\n\n" + "WHERE S.$SUBJECT_ID_COLUMN IN (?,?,?)" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(getArgs(resultQuery)).isEqualTo(subjectIds.toTypedArray()) @@ -71,11 +71,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with afterSubjectId`() { val afterSubjectId = "last-subject-id" - val subjectQuery = SubjectQuery(afterSubjectId = afterSubjectId) + val enrolmentRecordQuery = EnrolmentRecordQuery(afterSubjectId = afterSubjectId) val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S\n\n" + "WHERE S.$SUBJECT_ID_COLUMN > ?" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(afterSubjectId)) @@ -84,11 +84,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with projectId`() { val projectId = "test-project-id" - val subjectQuery = SubjectQuery(projectId = projectId) + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = projectId) val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S\n\n" + "WHERE S.$PROJECT_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(projectId)) @@ -97,11 +97,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with attendantId`() { val attendantId = "test-attendant-id".asTokenizableEncrypted() - val subjectQuery = SubjectQuery(attendantId = attendantId) + val enrolmentRecordQuery = EnrolmentRecordQuery(attendantId = attendantId) val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S\n\n" + "WHERE S.$ATTENDANT_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(attendantId.value)) @@ -110,11 +110,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with moduleId`() { val moduleId = "test-module-id".asTokenizableEncrypted() - val subjectQuery = SubjectQuery(moduleId = moduleId) + val enrolmentRecordQuery = EnrolmentRecordQuery(moduleId = moduleId) val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S\n\n" + "WHERE S.$MODULE_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(moduleId.value)) @@ -122,7 +122,7 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery with sort true`() { - val subjectQuery = SubjectQuery(sort = true) + val enrolmentRecordQuery = EnrolmentRecordQuery(sort = true) val expectedSql = """ SELECT * FROM $SUBJECT_TABLE_NAME S @@ -131,7 +131,7 @@ class RoomEnrolmentRecordQueryBuilderTest { ORDER BY S.$SUBJECT_ID_COLUMN ASC """.trimIndent() - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(resultQuery.argCount).isEqualTo(0) } @@ -140,12 +140,12 @@ class RoomEnrolmentRecordQueryBuilderTest { fun `buildSubjectQuery with multiple parameters and sort`() { val projectId = "proj1" val attendantId = "att1".asTokenizableEncrypted() - val subjectQuery = SubjectQuery(projectId = projectId, attendantId = attendantId, sort = true) + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = projectId, attendantId = attendantId, sort = true) val expectedSql = "SELECT * FROM $SUBJECT_TABLE_NAME S\n\n" + "WHERE S.$PROJECT_ID_COLUMN = ? AND S.$ATTENDANT_ID_COLUMN = ?\n" + "ORDER BY S.$SUBJECT_ID_COLUMN ASC" - val resultQuery = queryBuilder.buildSubjectQuery(subjectQuery) + val resultQuery = queryBuilder.buildSubjectQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(projectId, attendantId.value)) @@ -153,19 +153,19 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery throws error if format is set`() { - val subjectQuery = SubjectQuery(format = "ISO_19794_2_2005") + val enrolmentRecordQuery = EnrolmentRecordQuery(format = "ISO_19794_2_2005") val exception = assertThrows { - queryBuilder.buildSubjectQuery(subjectQuery) + queryBuilder.buildSubjectQuery(enrolmentRecordQuery) } assertThat(exception.message).isEqualTo("Cannot set format for subject query, use buildBiometricTemplatesQuery instead") } @Test fun `buildCountQuery with empty query`() { - val subjectQuery = SubjectQuery() + val enrolmentRecordQuery = EnrolmentRecordQuery() val expectedSql = "SELECT COUNT(DISTINCT S.$SUBJECT_ID_COLUMN) FROM $SUBJECT_TABLE_NAME S" - val resultQuery = queryBuilder.buildCountQuery(subjectQuery) + val resultQuery = queryBuilder.buildCountQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(resultQuery.argCount).isEqualTo(0) @@ -174,11 +174,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildCountQuery with subjectId`() { val subjectId = "s1" - val subjectQuery = SubjectQuery(subjectId = subjectId) + val enrolmentRecordQuery = EnrolmentRecordQuery(subjectId = subjectId) val expectedSql = "SELECT COUNT(DISTINCT S.$SUBJECT_ID_COLUMN) FROM $SUBJECT_TABLE_NAME S WHERE S.$SUBJECT_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildCountQuery(subjectQuery) + val resultQuery = queryBuilder.buildCountQuery(enrolmentRecordQuery) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(subjectId)) @@ -187,11 +187,11 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildCountQuery with projectId`() { val projectId = "p1" - val subjectQuery = SubjectQuery(projectId = projectId) + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = projectId) val expectedSql = "SELECT COUNT(DISTINCT S.$SUBJECT_ID_COLUMN) FROM $SUBJECT_TABLE_NAME S WHERE S.$PROJECT_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildCountQuery(subjectQuery) + val resultQuery = queryBuilder.buildCountQuery(enrolmentRecordQuery) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(projectId)) @@ -200,12 +200,12 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildCountQuery with format`() { val format = "ISO_FP" - val subjectQuery = SubjectQuery(format = format) + val enrolmentRecordQuery = EnrolmentRecordQuery(format = format) val expectedSql = "SELECT COUNT(DISTINCT S.$SUBJECT_ID_COLUMN) FROM $SUBJECT_TABLE_NAME S INNER JOIN $TEMPLATE_TABLE_NAME T" + " using(subjectId) WHERE T.$FORMAT_COLUMN = ?" - val resultQuery = queryBuilder.buildCountQuery(subjectQuery) + val resultQuery = queryBuilder.buildCountQuery(enrolmentRecordQuery) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(format)) @@ -215,12 +215,12 @@ class RoomEnrolmentRecordQueryBuilderTest { fun `buildCountQuery with projectId and format`() { val projectId = "p1" val format = "ISO_FP" - val subjectQuery = SubjectQuery(projectId = projectId, format = format) + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = projectId, format = format) val expectedSql = "SELECT COUNT(DISTINCT S.$SUBJECT_ID_COLUMN) FROM $SUBJECT_TABLE_NAME S INNER JOIN $TEMPLATE_TABLE_NAME T" + " using(subjectId) WHERE S.$PROJECT_ID_COLUMN = ? AND T.$FORMAT_COLUMN = ?" - val resultQuery = queryBuilder.buildCountQuery(subjectQuery) + val resultQuery = queryBuilder.buildCountQuery(enrolmentRecordQuery) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(projectId, format)) @@ -228,10 +228,10 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildBiometricTemplatesQuery throws error if format is not set`() { - val subjectQuery = SubjectQuery() + val enrolmentRecordQuery = EnrolmentRecordQuery() val pageSize = 10 val exception = assertThrows { - queryBuilder.buildBiometricTemplatesQuery(subjectQuery, pageSize) + queryBuilder.buildBiometricTemplatesQuery(enrolmentRecordQuery, pageSize) } assertThat( exception.message, @@ -242,7 +242,7 @@ class RoomEnrolmentRecordQueryBuilderTest { fun `buildBiometricTemplatesQuery with format`() { val format = "ISO_FP_TEMPLATE" val pageSize = 10 - val subjectQuery = SubjectQuery(format = format) + val enrolmentRecordQuery = EnrolmentRecordQuery(format = format) val expectedSql = """ SELECT A.* @@ -257,7 +257,7 @@ class RoomEnrolmentRecordQueryBuilderTest { ) B USING(subjectId) where A.format ='$format' """.trimIndent() - val resultQuery = queryBuilder.buildBiometricTemplatesQuery(subjectQuery, pageSize) + val resultQuery = queryBuilder.buildBiometricTemplatesQuery(enrolmentRecordQuery, pageSize) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(format)) @@ -267,7 +267,7 @@ class RoomEnrolmentRecordQueryBuilderTest { fun `buildBiometricTemplatesQuery uses sort true internally`() { val format = "ANY_FORMAT" val pageSize = 15 - val subjectQuery = SubjectQuery(format = format, sort = false) + val enrolmentRecordQuery = EnrolmentRecordQuery(format = format, sort = false) val expectedSql = """ SELECT A.* @@ -282,7 +282,7 @@ class RoomEnrolmentRecordQueryBuilderTest { ) B USING(subjectId) where A.format ='$format' """.trimIndent() - val resultQuery = queryBuilder.buildBiometricTemplatesQuery(subjectQuery, pageSize) + val resultQuery = queryBuilder.buildBiometricTemplatesQuery(enrolmentRecordQuery, pageSize) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(format)) @@ -290,19 +290,19 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildDeleteQuery throws error if format is set`() { - val subjectQuery = SubjectQuery(format = "ISO_19794_2_2005") + val enrolmentRecordQuery = EnrolmentRecordQuery(format = "ISO_19794_2_2005") val exception = assertThrows { - queryBuilder.buildDeleteQuery(subjectQuery) + queryBuilder.buildDeleteQuery(enrolmentRecordQuery) } assertThat(exception.message).isEqualTo("format is not supported for deletion") } @Test fun `buildDeleteQuery with empty query`() { - val subjectQuery = SubjectQuery() + val enrolmentRecordQuery = EnrolmentRecordQuery() val expectedSql = "DELETE FROM DbSubject" - val resultQuery = queryBuilder.buildDeleteQuery(subjectQuery) + val resultQuery = queryBuilder.buildDeleteQuery(enrolmentRecordQuery) assertThat(resultQuery.sql.trim()).isEqualTo(expectedSql.trim()) assertThat(resultQuery.argCount).isEqualTo(0) @@ -311,10 +311,10 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildDeleteQuery with subjectId`() { val subjectId = "id-to-delete" - val subjectQuery = SubjectQuery(subjectId = subjectId) + val enrolmentRecordQuery = EnrolmentRecordQuery(subjectId = subjectId) val expectedSql = "DELETE FROM DbSubject WHERE $SUBJECT_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildDeleteQuery(subjectQuery) + val resultQuery = queryBuilder.buildDeleteQuery(enrolmentRecordQuery) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(subjectId)) @@ -324,9 +324,9 @@ class RoomEnrolmentRecordQueryBuilderTest { fun `buildDeleteQuery with projectId and moduleId`() { val projectId = "proj-del" val moduleId = "mod-del".asTokenizableEncrypted() - val subjectQuery = SubjectQuery(projectId = projectId, moduleId = moduleId) + val enrolmentRecordQuery = EnrolmentRecordQuery(projectId = projectId, moduleId = moduleId) val expectedSql = "DELETE FROM DbSubject WHERE $PROJECT_ID_COLUMN = ? AND $MODULE_ID_COLUMN = ?" - val resultQuery = queryBuilder.buildDeleteQuery(subjectQuery) + val resultQuery = queryBuilder.buildDeleteQuery(enrolmentRecordQuery) assertThat(resultQuery.sql).isEqualTo(expectedSql) assertThat(getArgs(resultQuery)).isEqualTo(arrayOf(projectId, moduleId.value)) } @@ -334,7 +334,7 @@ class RoomEnrolmentRecordQueryBuilderTest { @Test fun `buildSubjectQuery includes credential join clause when externalCredential is provided`() { val credentialValue = "credentialValue" - val query = SubjectQuery( + val query = EnrolmentRecordQuery( projectId = "projectId", externalCredential = credentialValue.asTokenizableEncrypted(), ) diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCaseTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCaseTest.kt index 59f941427f..3bc90f58bd 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCaseTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/InsertRecordsInRoomDuringMigrationUseCaseTest.kt @@ -1,7 +1,7 @@ package com.simprints.infra.enrolment.records.repository.local.migration import com.simprints.infra.config.store.models.Project -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.enrolment.records.repository.local.RoomEnrolmentRecordLocalDataSource import io.mockk.* import io.mockk.impl.annotations.MockK @@ -29,25 +29,25 @@ class InsertRecordsInRoomDuringMigrationUseCaseTest { @Test fun `invoke should call performActions when migration is in progress`() = runBlocking { - val subjectAction = listOf(mockk(relaxed = true)) + val enrolmentRecordActions = listOf(mockk(relaxed = true)) val project = mockk() coEvery { realmToRoomMigrationFlagsStore.isMigrationInProgress() } returns true coJustRun { roomEnrolmentRecordLocalDataSource.performActions(any(), any()) } - insertRecordsInRoomDuringMigrationUseCase.invoke(subjectAction, project) + insertRecordsInRoomDuringMigrationUseCase.invoke(enrolmentRecordActions, project) - coVerify { roomEnrolmentRecordLocalDataSource.performActions(actions = subjectAction, project = project) } + coVerify { roomEnrolmentRecordLocalDataSource.performActions(actions = enrolmentRecordActions, project = project) } } @Test fun `invoke should not call performActions when migration is not in progress`() = runBlocking { - val subjectAction = listOf(mockk()) + val enrolmentRecordActions = listOf(mockk()) val project = mockk() coEvery { realmToRoomMigrationFlagsStore.isMigrationInProgress() } returns false - insertRecordsInRoomDuringMigrationUseCase.invoke(subjectAction, project) + insertRecordsInRoomDuringMigrationUseCase.invoke(enrolmentRecordActions, project) coVerify(exactly = 0) { roomEnrolmentRecordLocalDataSource.performActions(any(), any()) } } diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorkerTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorkerTest.kt index c9d4160ef6..242390921b 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorkerTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/migration/RealmToRoomMigrationWorkerTest.kt @@ -6,8 +6,8 @@ import androidx.work.ListenableWorker.Result import androidx.work.WorkerParameters import com.google.common.truth.Truth.assertThat import com.simprints.infra.config.store.ConfigRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.enrolment.records.repository.local.RealmEnrolmentRecordLocalDataSource import com.simprints.infra.enrolment.records.repository.local.RoomEnrolmentRecordLocalDataSource import io.mockk.MockKAnnotations @@ -94,8 +94,8 @@ class RealmToRoomMigrationWorkerTest { @Test fun `doWork should succeed and migrate data when realm has data`() = runTest { - val mockSubjectsBatch1 = listOf(mockk(), mockk()) - val mockSubjectsBatch2 = listOf(mockk()) + val mockSubjectsBatch1 = listOf(mockk(), mockk()) + val mockSubjectsBatch2 = listOf(mockk()) // Given coEvery { realmToRoomMigrationFlagsStore.isDownSyncInProgress() } returns false @@ -116,7 +116,7 @@ class RealmToRoomMigrationWorkerTest { coVerify(exactly = 1) { roomDataSource.performActions( match { actions -> - actions.all { it is SubjectAction.Creation } && actions.size == mockSubjectsBatch1.size + actions.all { it is EnrolmentRecordAction.Creation } && actions.size == mockSubjectsBatch1.size }, any(), ) @@ -124,7 +124,7 @@ class RealmToRoomMigrationWorkerTest { coVerify(exactly = 1) { roomDataSource.performActions( match { actions -> - actions.all { it is SubjectAction.Creation } && actions.size == mockSubjectsBatch2.size + actions.all { it is EnrolmentRecordAction.Creation } && actions.size == mockSubjectsBatch2.size }, any(), ) @@ -153,8 +153,8 @@ class RealmToRoomMigrationWorkerTest { @Test fun `doWork should fail and increment retry count when migration fails in processRecords`() = runTest { - val mockSubjectsBatch1 = listOf(mockk(), mockk()) - val mockSubjectsBatch2 = listOf(mockk()) + val mockSubjectsBatch1 = listOf(mockk(), mockk()) + val mockSubjectsBatch2 = listOf(mockk()) // Given coEvery { realmToRoomMigrationFlagsStore.isDownSyncInProgress() } returns false @@ -175,8 +175,8 @@ class RealmToRoomMigrationWorkerTest { @Test fun `doWork should fail and increment retry count when migration fails in validateRealmToRoomMigration`() = runTest { - val mockSubjectsBatch1 = listOf(mockk(), mockk()) - val mockSubjectsBatch2 = listOf(mockk()) + val mockSubjectsBatch1 = listOf(mockk(), mockk()) + val mockSubjectsBatch2 = listOf(mockk()) // Given coEvery { realmToRoomMigrationFlagsStore.isDownSyncInProgress() } returns false diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/models/DbSubjectTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/models/DbSubjectTest.kt index ba4ce21f7e..e5e7bb5fce 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/models/DbSubjectTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/local/models/DbSubjectTest.kt @@ -2,13 +2,14 @@ package com.simprints.infra.enrolment.records.repository.local.models import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.infra.enrolment.records.realm.store.models.DbFaceSample import com.simprints.infra.enrolment.records.realm.store.models.DbFingerprintSample import com.simprints.infra.enrolment.records.realm.store.models.DbSubject -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import io.realm.kotlin.ext.realmListOf import io.realm.kotlin.types.RealmInstant import io.realm.kotlin.types.RealmUUID @@ -27,31 +28,39 @@ class DbSubjectTest { @Test fun fromDomainToDbModel() { - val fingerprintSample = Sample( - identifier = SampleIdentifier.RIGHT_3RD_FINGER, - template = Random.nextBytes(64), + val fingerprintSample = BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.RIGHT_3RD_FINGER, + template = Random.nextBytes(64), + ), + ), format = "NEC_1", referenceId = REFERENCE_ID, modality = Modality.FINGERPRINT, ) - val faceSample = Sample( - template = Random.nextBytes(64), + val faceSample = BiometricReference( + templates = listOf( + BiometricTemplate( + template = Random.nextBytes(64), + ), + ), format = "RANK_ONE_1_23", referenceId = REFERENCE_ID, modality = Modality.FACE, ) - val domainSubject = Subject( + val domainEnrolmentRecord = EnrolmentRecord( subjectId = GUID, projectId = PROJECT_ID, attendantId = ATTENDANT_ID, moduleId = MODULE_ID, createdAt = Date(0), updatedAt = Date(1500), - samples = listOf(fingerprintSample, faceSample), + references = listOf(fingerprintSample, faceSample), ) - val dbSubject = domainSubject.toRealmDb() + val dbSubject = domainEnrolmentRecord.toRealmDb() with(dbSubject) { assertThat(subjectId).isEqualTo(RealmUUID.from(GUID)) @@ -60,9 +69,9 @@ class DbSubjectTest { assertThat(moduleId).isEqualTo(MODULE_ID.value) assertThat(createdAt).isEqualTo(RealmInstant.from(0, 0)) assertThat(updatedAt).isEqualTo(RealmInstant.from(1, 500_000_000)) - assertThat(fingerprintSamples.first().id).isEqualTo(fingerprintSample.id) + assertThat(fingerprintSamples.first().id).isEqualTo(fingerprintSample.templates.first().id) assertThat(fingerprintSamples.first().referenceId).isEqualTo(REFERENCE_ID) - assertThat(faceSamples.first().id).isEqualTo(faceSample.id) + assertThat(faceSamples.first().id).isEqualTo(faceSample.templates.first().id) assertThat(faceSamples.first().referenceId).isEqualTo(REFERENCE_ID) } } @@ -70,7 +79,7 @@ class DbSubjectTest { @Test fun fromDbModelToDomain() { val fingerprintSample = DbFingerprintSample().apply { - fingerIdentifier = SampleIdentifier.RIGHT_3RD_FINGER.ordinal + fingerIdentifier = TemplateIdentifier.RIGHT_3RD_FINGER.ordinal template = Random.nextBytes(64) format = "NEC_1" referenceId = REFERENCE_ID @@ -103,9 +112,15 @@ class DbSubjectTest { assertThat(updatedAt).isEqualTo(Date(1500)) assertThat(moduleId).isEqualTo(MODULE_ID) assertThat(projectId).isEqualTo(PROJECT_ID) - assertThat(samples.first { it.modality == Modality.FINGERPRINT }.id).isEqualTo(fingerprintSample.id) - assertThat(samples.first { it.modality == Modality.FINGERPRINT }.referenceId).isEqualTo(REFERENCE_ID) - assertThat(samples.first { it.modality == Modality.FACE }.referenceId).isEqualTo(REFERENCE_ID) + assertThat( + references + .first { it.modality == Modality.FINGERPRINT } + .templates + .first() + .id, + ).isEqualTo(fingerprintSample.id) + assertThat(references.first { it.modality == Modality.FINGERPRINT }.referenceId).isEqualTo(REFERENCE_ID) + assertThat(references.first { it.modality == Modality.FACE }.referenceId).isEqualTo(REFERENCE_ID) } } } diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImplTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImplTest.kt index cd97b91c64..4b399a8268 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImplTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/EnrolmentRecordRemoteDataSourceImplTest.kt @@ -2,12 +2,13 @@ package com.simprints.infra.enrolment.records.repository.remote import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.tools.utils.EncodingUtils import com.simprints.infra.authstore.AuthStore -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.enrolment.records.repository.remote.models.ApiEnrolmentRecord import com.simprints.infra.enrolment.records.repository.remote.models.ApiEnrolmentRecords import com.simprints.infra.enrolment.records.repository.remote.models.face.ApiFaceReference @@ -63,21 +64,29 @@ class EnrolmentRecordRemoteDataSourceImplTest { @Test fun `Upload successfully the records`() = runTest { - val subject = Subject( + val enrolmentRecord = EnrolmentRecord( subjectId = SUBJECT_ID, projectId = PROJECT_ID, moduleId = MODULE_ID, attendantId = ATTENDANT_ID, - samples = listOf( - Sample( - identifier = SampleIdentifier.LEFT_3RD_FINGER, - template = FINGERPRINT_TEMPLATE, + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = TemplateIdentifier.LEFT_3RD_FINGER, + template = FINGERPRINT_TEMPLATE, + ), + ), format = "ISO_19794_2", referenceId = "5289df73-7df5-3326-bcdd-22597afb1fac", modality = Modality.FINGERPRINT, ), - Sample( - template = FACE_TEMPLATE, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = FACE_TEMPLATE, + ), + ), format = "faceTemplateFormat", referenceId = "b4a3ba90-6413-32b4-a4ea-a841a5a400ec", modality = Modality.FACE, @@ -106,7 +115,7 @@ class EnrolmentRecordRemoteDataSourceImplTest { ), ), ) - enrolmentRecordRemoteDataSourceImpl.uploadRecords(listOf(subject)) + enrolmentRecordRemoteDataSourceImpl.uploadRecords(listOf(enrolmentRecord)) coVerify(exactly = 1) { remoteInterface.uploadRecords( diff --git a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerTest.kt b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerTest.kt index 3b846e988b..2f934a2549 100644 --- a/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerTest.kt +++ b/infra/enrolment-records/repository/src/test/java/com/simprints/infra/enrolment/records/repository/remote/models/fingerprint/ApiFingerTest.kt @@ -1,23 +1,23 @@ package com.simprints.infra.enrolment.records.repository.remote.models.fingerprint import com.google.common.truth.Truth.assertThat -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import org.junit.Test class ApiFingerTest { @Test fun `should map correctly the Finger enums`() { val mapping = mapOf( - SampleIdentifier.LEFT_THUMB to ApiFinger.LEFT_THUMB, - SampleIdentifier.LEFT_INDEX_FINGER to ApiFinger.LEFT_INDEX_FINGER, - SampleIdentifier.LEFT_3RD_FINGER to ApiFinger.LEFT_3RD_FINGER, - SampleIdentifier.LEFT_4TH_FINGER to ApiFinger.LEFT_4TH_FINGER, - SampleIdentifier.LEFT_5TH_FINGER to ApiFinger.LEFT_5TH_FINGER, - SampleIdentifier.RIGHT_THUMB to ApiFinger.RIGHT_THUMB, - SampleIdentifier.RIGHT_INDEX_FINGER to ApiFinger.RIGHT_INDEX_FINGER, - SampleIdentifier.RIGHT_3RD_FINGER to ApiFinger.RIGHT_3RD_FINGER, - SampleIdentifier.RIGHT_4TH_FINGER to ApiFinger.RIGHT_4TH_FINGER, - SampleIdentifier.RIGHT_5TH_FINGER to ApiFinger.RIGHT_5TH_FINGER, + TemplateIdentifier.LEFT_THUMB to ApiFinger.LEFT_THUMB, + TemplateIdentifier.LEFT_INDEX_FINGER to ApiFinger.LEFT_INDEX_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER to ApiFinger.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_4TH_FINGER to ApiFinger.LEFT_4TH_FINGER, + TemplateIdentifier.LEFT_5TH_FINGER to ApiFinger.LEFT_5TH_FINGER, + TemplateIdentifier.RIGHT_THUMB to ApiFinger.RIGHT_THUMB, + TemplateIdentifier.RIGHT_INDEX_FINGER to ApiFinger.RIGHT_INDEX_FINGER, + TemplateIdentifier.RIGHT_3RD_FINGER to ApiFinger.RIGHT_3RD_FINGER, + TemplateIdentifier.RIGHT_4TH_FINGER to ApiFinger.RIGHT_4TH_FINGER, + TemplateIdentifier.RIGHT_5TH_FINGER to ApiFinger.RIGHT_5TH_FINGER, ) mapping.forEach { diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCaptureBiometricsPayload.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCaptureBiometricsPayload.kt index 2e68ff8e17..f24e138b04 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCaptureBiometricsPayload.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCaptureBiometricsPayload.kt @@ -1,7 +1,7 @@ package com.simprints.infra.eventsync.event.remote.models import androidx.annotation.Keep -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureBiometricsEvent @@ -13,7 +13,7 @@ internal data class ApiFingerprintCaptureBiometricsPayload( ) : ApiEventPayload(startTime) { @Keep data class Fingerprint( - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val template: String, val quality: Int, val format: String, diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayload.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayload.kt index 28ac3f3cf3..9294e24192 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayload.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayload.kt @@ -3,7 +3,7 @@ package com.simprints.infra.eventsync.event.remote.models import androidx.annotation.Keep import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonInclude.Include -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureEvent.FingerprintCapturePayload import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureEvent.FingerprintCapturePayload.Result.BAD_QUALITY @@ -20,13 +20,13 @@ internal data class ApiFingerprintCapturePayload( override val startTime: ApiTimestamp, val endTime: ApiTimestamp?, val qualityThreshold: Int, - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val result: ApiResult, val fingerprint: ApiFingerprint?, ) : ApiEventPayload(startTime) { @Keep data class ApiFingerprint( - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val quality: Int, val format: String, ) { diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/ApiBiometricReference.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/ApiBiometricReference.kt index 6c0ecd7be0..d5d8123e0e 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/ApiBiometricReference.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/ApiBiometricReference.kt @@ -4,7 +4,7 @@ import androidx.annotation.Keep import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.events.event.domain.models.subject.FaceTemplate import com.simprints.infra.events.event.domain.models.subject.FingerprintTemplate import com.simprints.infra.eventsync.event.remote.models.subject.biometricref.face.ApiFaceReference @@ -58,4 +58,4 @@ internal fun FaceTemplate.fromDomainToApi() = ApiFaceTemplate(template) internal fun ApiFingerprintReference.fromApiToDomain() = DomainFingerprintReference(id, templates.map { it.fromApiToDomain() }, format, metadata) -internal fun ApiFingerprintTemplate.fromApiToDomain() = FingerprintTemplate(template, SampleIdentifier.valueOf(finger.name)) +internal fun ApiFingerprintTemplate.fromApiToDomain() = FingerprintTemplate(template, TemplateIdentifier.valueOf(finger.name)) diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/fingerprint/ApiFingerprintTemplate.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/fingerprint/ApiFingerprintTemplate.kt index 89786f8b89..0c3fd20b38 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/fingerprint/ApiFingerprintTemplate.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/remote/models/subject/biometricref/fingerprint/ApiFingerprintTemplate.kt @@ -1,10 +1,10 @@ package com.simprints.infra.eventsync.event.remote.models.subject.biometricref.fingerprint import androidx.annotation.Keep -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier @Keep internal data class ApiFingerprintTemplate( val template: String, - val finger: SampleIdentifier, + val finger: TemplateIdentifier, ) diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/common/SubjectFactory.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/common/EnrolmentRecordFactory.kt similarity index 52% rename from infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/common/SubjectFactory.kt rename to infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/common/EnrolmentRecordFactory.kt index 7c431c6a15..f7e088b8e1 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/common/SubjectFactory.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/common/EnrolmentRecordFactory.kt @@ -2,143 +2,144 @@ package com.simprints.infra.eventsync.sync.common import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.utils.EncodingUtils -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.events.event.domain.models.subject.BiometricReference import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordCreationEvent.EnrolmentRecordCreationPayload import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordMoveEvent.EnrolmentRecordCreationInMove import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordUpdateEvent.EnrolmentRecordUpdatePayload import com.simprints.infra.events.event.domain.models.subject.FaceReference -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 java.util.Date import javax.inject.Inject +import com.simprints.core.domain.reference.BiometricReference as CoreBiometricReference -class SubjectFactory @Inject constructor( +class EnrolmentRecordFactory @Inject constructor( private val encodingUtils: EncodingUtils, private val timeHelper: TimeHelper, ) { - fun buildSubjectFromCreationPayload(payload: EnrolmentRecordCreationPayload) = with(payload) { - buildSubject( + fun buildFromCreationPayload(payload: EnrolmentRecordCreationPayload) = with(payload) { + buildEnrolmentRecord( subjectId = subjectId, projectId = projectId, attendantId = attendantId, moduleId = moduleId, - samples = extractSamplesFromBiometricReferences(this.biometricReferences), + references = extractFromBiometricReferences(this.biometricReferences), externalCredentials = payload.externalCredentials, ) } - fun buildSubjectFromMovePayload(payload: EnrolmentRecordCreationInMove) = with(payload) { - buildSubject( + fun buildFromMovePayload(payload: EnrolmentRecordCreationInMove) = with(payload) { + buildEnrolmentRecord( subjectId = subjectId, projectId = projectId, attendantId = attendantId, moduleId = moduleId, - samples = extractSamplesFromBiometricReferences(this.biometricReferences), + references = extractFromBiometricReferences(this.biometricReferences), externalCredentials = externalCredential?.let { listOf(it) } ?: emptyList(), ) } - fun buildSubjectFromUpdatePayload( - existingSubject: Subject, + fun buildFromUpdatePayload( + existingEnrolmentRecord: EnrolmentRecord, payload: EnrolmentRecordUpdatePayload, - ): Subject { + ): EnrolmentRecord { val removedBiometricReferences = payload.biometricReferencesRemoved.toSet() // to make lookup O(1) - val addedSamples = extractSamplesFromBiometricReferences(payload.biometricReferencesAdded) + val addedSamples = extractFromBiometricReferences(payload.biometricReferencesAdded) val externalCredentialsAdded = payload.externalCredentialsAdded - return existingSubject.copy( - samples = existingSubject.samples + return existingEnrolmentRecord.copy( + references = existingEnrolmentRecord.references .filterNot { it.referenceId in removedBiometricReferences } .plus(addedSamples), - externalCredentials = existingSubject.externalCredentials + externalCredentials = existingEnrolmentRecord.externalCredentials .plus(externalCredentialsAdded) .distinctBy { it.value.value }, ) } - fun buildSubjectFromCaptureResults( + fun buildFromCaptureResults( subjectId: String, projectId: String, attendantId: TokenizableString, moduleId: TokenizableString, - captures: List, + captures: List, externalCredential: ExternalCredential?, - ): Subject = buildSubject( + ): EnrolmentRecord = buildEnrolmentRecord( subjectId = subjectId, projectId = projectId, attendantId = attendantId, moduleId = moduleId, createdAt = Date(timeHelper.now().ms), - samples = captures.flatMap { extractCaptureSamples(it) }, + references = captures.map { extractFromCapture(it) }, externalCredentials = externalCredential?.let { listOf(it) } ?: emptyList(), ) - fun buildSubject( + fun buildEnrolmentRecord( subjectId: String, projectId: String, attendantId: TokenizableString, moduleId: TokenizableString, createdAt: Date? = null, updatedAt: Date? = null, - samples: List = emptyList(), + references: List = emptyList(), externalCredentials: List = emptyList(), - ) = Subject( + ) = EnrolmentRecord( subjectId = subjectId, projectId = projectId, attendantId = attendantId, moduleId = moduleId, createdAt = createdAt, updatedAt = updatedAt, - samples = samples, + references = references, externalCredentials = externalCredentials, ) - private fun extractCaptureSamples(response: CaptureIdentity) = response.samples.map { sample -> - Sample( - identifier = sample.identifier, - template = sample.template, - format = sample.format, - referenceId = response.referenceId, - modality = sample.modality, - ) - } + private fun extractFromCapture(response: BiometricReferenceCapture) = CoreBiometricReference( + referenceId = response.referenceId, + format = response.format, + modality = response.modality, + templates = response.templates.map { + BiometricTemplate( + identifier = it.identifier, + template = it.template, + ) + }, + ) - fun extractSamplesFromBiometricReferences(biometricReferences: List?) = biometricReferences + fun extractFromBiometricReferences(biometricReferences: List?) = biometricReferences ?.map { reference -> when (reference) { - is FingerprintReference -> reference.templates.map { buildFingerprintSample(it, reference.format, reference.id) } - is FaceReference -> reference.templates.map { buildFaceSample(it, reference.format, reference.id) } + is FingerprintReference -> buildCoreBiometricReference(reference) + is FaceReference -> buildCoreBiometricReference(reference) } - }?.flatten() + } ?: emptyList() - private fun buildFingerprintSample( - template: FingerprintTemplate, - format: String, - referenceId: String, - ): Sample = Sample( - identifier = template.finger, - template = encodingUtils.base64ToBytes(template.template), - format = format, - referenceId = referenceId, + private fun buildCoreBiometricReference(reference: FingerprintReference): CoreBiometricReference = CoreBiometricReference( + referenceId = reference.id, + format = reference.format, modality = Modality.FINGERPRINT, + templates = reference.templates.map { + BiometricTemplate( + identifier = it.finger, + template = encodingUtils.base64ToBytes(it.template), + ) + }, ) - private fun buildFaceSample( - template: FaceTemplate, - format: String, - referenceId: String, - ) = Sample( - template = encodingUtils.base64ToBytes(template.template), - format = format, - referenceId = referenceId, + private fun buildCoreBiometricReference(reference: FaceReference) = CoreBiometricReference( + referenceId = reference.id, + format = reference.format, modality = Modality.FACE, + templates = reference.templates.map { + BiometricTemplate( + template = encodingUtils.base64ToBytes(it.template), + ) + }, ) } diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/BaseEventDownSyncTask.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/BaseEventDownSyncTask.kt index cf2a034b7e..346c128cb7 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/BaseEventDownSyncTask.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/BaseEventDownSyncTask.kt @@ -6,9 +6,9 @@ import com.simprints.core.tools.time.Timestamp import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Creation -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Deletion +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Creation +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Deletion import com.simprints.infra.events.EventRepository import com.simprints.infra.events.event.domain.models.downsync.EventDownSyncRequestEvent import com.simprints.infra.events.event.domain.models.scope.EventScope @@ -24,8 +24,8 @@ import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.COMPLETE import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.FAILED import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.RUNNING +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import com.simprints.infra.eventsync.sync.common.EventDownSyncProgress -import com.simprints.infra.eventsync.sync.common.SubjectFactory import com.simprints.infra.logging.LoggingConstants.CrashReportTag.SYNC import com.simprints.infra.logging.Simber import kotlinx.coroutines.CoroutineScope @@ -37,7 +37,7 @@ import java.util.UUID internal abstract class BaseEventDownSyncTask( protected val enrolmentRecordRepository: EnrolmentRecordRepository, protected val eventDownSyncScopeRepository: EventDownSyncScopeRepository, - protected val subjectFactory: SubjectFactory, + protected val enrolmentRecordFactory: EnrolmentRecordFactory, protected val configManager: ConfigManager, protected val timeHelper: TimeHelper, protected val eventRepository: EventRepository, @@ -200,9 +200,9 @@ internal abstract class BaseEventDownSyncTask( // Default implementation does nothing } - private fun handleSubjectCreationEvent(event: EnrolmentRecordCreationEvent): List { - val subject = subjectFactory.buildSubjectFromCreationPayload(event.payload) - return if (subject.samples.isNotEmpty()) { + private fun handleSubjectCreationEvent(event: EnrolmentRecordCreationEvent): List { + val subject = enrolmentRecordFactory.buildFromCreationPayload(event.payload) + return if (subject.references.isNotEmpty()) { listOf(Creation(subject)) } else { emptyList() @@ -212,11 +212,11 @@ internal abstract class BaseEventDownSyncTask( private suspend fun handleSubjectMoveEvent( operation: EventDownSyncOperation, event: EnrolmentRecordMoveEvent, - ): List { + ): List { val enrolmentRecordDeletion = event.payload.enrolmentRecordDeletion val enrolmentRecordCreation = event.payload.enrolmentRecordCreation - val actions = mutableListOf() + val actions = mutableListOf() actions.add(Deletion(enrolmentRecordDeletion.subjectId)) if (shouldBeSynced(enrolmentRecordCreation, operation)) { @@ -258,22 +258,22 @@ internal abstract class BaseEventDownSyncTask( private fun createASubjectActionFromRecordCreation(enrolmentRecordCreation: EnrolmentRecordCreationInMove?): Creation? = enrolmentRecordCreation?.let { - val subject = subjectFactory.buildSubjectFromMovePayload(it) - if (subject.samples.isNotEmpty()) { + val subject = enrolmentRecordFactory.buildFromMovePayload(it) + if (subject.references.isNotEmpty()) { Creation(subject) } else { null } } - private fun handleSubjectDeletionEvent(event: EnrolmentRecordDeletionEvent): List = + private fun handleSubjectDeletionEvent(event: EnrolmentRecordDeletionEvent): List = listOf(Deletion(event.payload.subjectId)) - private fun handleSubjectUpdateEvent(event: EnrolmentRecordUpdateEvent): List = with(event.payload) { + private fun handleSubjectUpdateEvent(event: EnrolmentRecordUpdateEvent): List = with(event.payload) { listOf( - SubjectAction.Update( + EnrolmentRecordAction.Update( subjectId = subjectId, - samplesToAdd = subjectFactory.extractSamplesFromBiometricReferences(biometricReferencesAdded), + samplesToAdd = enrolmentRecordFactory.extractFromBiometricReferences(biometricReferencesAdded), referenceIdsToRemove = biometricReferencesRemoved, externalCredentialsToAdd = externalCredentialsAdded, externalCredentialIdsToRemove = emptyList(), // Only used locally to ensure a single credential is linked per session diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTask.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTask.kt index dd91040edf..ab9476eb5d 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTask.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTask.kt @@ -8,7 +8,7 @@ import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordEve import com.simprints.infra.eventsync.event.commcare.CommCareEventDataSource import com.simprints.infra.eventsync.status.down.EventDownSyncScopeRepository import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import com.simprints.infra.logging.LoggingConstants.CrashReportTag.COMMCARE_SYNC import com.simprints.infra.logging.Simber import kotlinx.coroutines.CoroutineScope @@ -17,7 +17,7 @@ import javax.inject.Inject internal class CommCareEventSyncTask @Inject constructor( enrolmentRecordRepository: EnrolmentRecordRepository, eventDownSyncScopeRepository: EventDownSyncScopeRepository, - subjectFactory: SubjectFactory, + enrolmentRecordFactory: EnrolmentRecordFactory, configManager: ConfigManager, timeHelper: TimeHelper, eventRepository: EventRepository, @@ -25,7 +25,7 @@ internal class CommCareEventSyncTask @Inject constructor( ) : BaseEventDownSyncTask( enrolmentRecordRepository, eventDownSyncScopeRepository, - subjectFactory, + enrolmentRecordFactory, configManager, timeHelper, eventRepository, diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTask.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTask.kt index 0973e0ba60..c6fd03d582 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTask.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTask.kt @@ -8,7 +8,7 @@ import com.simprints.infra.events.EventRepository import com.simprints.infra.eventsync.event.remote.EventRemoteDataSource import com.simprints.infra.eventsync.status.down.EventDownSyncScopeRepository import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.consumeAsFlow @@ -17,20 +17,19 @@ import javax.inject.Inject internal class SimprintsEventDownSyncTask @Inject constructor( enrolmentRecordRepository: EnrolmentRecordRepository, eventDownSyncScopeRepository: EventDownSyncScopeRepository, - subjectFactory: SubjectFactory, + enrolmentRecordFactory: EnrolmentRecordFactory, configManager: ConfigManager, timeHelper: TimeHelper, eventRepository: EventRepository, private val eventRemoteDataSource: EventRemoteDataSource, ) : BaseEventDownSyncTask( - enrolmentRecordRepository, - eventDownSyncScopeRepository, - subjectFactory, - configManager, - timeHelper, - eventRepository, -) { - + enrolmentRecordRepository, + eventDownSyncScopeRepository, + enrolmentRecordFactory, + configManager, + timeHelper, + eventRepository, + ) { override suspend fun fetchEvents( operation: EventDownSyncOperation, scope: CoroutineScope, diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayloadTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayloadTest.kt index 8b92671e1a..f6a0da0a60 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayloadTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/ApiFingerprintCapturePayloadTest.kt @@ -1,7 +1,7 @@ package com.simprints.infra.eventsync.event.remote.models import com.google.common.truth.Truth.assertThat -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.tools.utils.randomUUID import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureEvent @@ -13,7 +13,7 @@ class ApiFingerprintCapturePayloadTest { fun `creating fingerprint capture object has correct values`() { val fingerprint = ApiFingerprintCapturePayload.ApiFingerprint( FingerprintCaptureEvent.FingerprintCapturePayload.Fingerprint( - finger = SampleIdentifier.LEFT_3RD_FINGER, + finger = TemplateIdentifier.LEFT_3RD_FINGER, quality = 23, format = "ISO_19794_2", ), @@ -23,7 +23,7 @@ class ApiFingerprintCapturePayloadTest { startTime = ApiTimestamp(1), endTime = ApiTimestamp(1), qualityThreshold = 23, - finger = SampleIdentifier.LEFT_3RD_FINGER, + finger = TemplateIdentifier.LEFT_3RD_FINGER, result = ApiFingerprintCapturePayload.ApiResult.GOOD_SCAN, fingerprint = fingerprint, ) @@ -33,7 +33,7 @@ class ApiFingerprintCapturePayloadTest { assertThat(startTime).isEqualTo(ApiTimestamp(1)) assertThat(endTime).isEqualTo(ApiTimestamp(1)) assertThat(qualityThreshold).isEqualTo(23) - assertThat(finger).isEqualTo(SampleIdentifier.LEFT_3RD_FINGER) + assertThat(finger).isEqualTo(TemplateIdentifier.LEFT_3RD_FINGER) assertThat(result).isEqualTo(ApiFingerprintCapturePayload.ApiResult.GOOD_SCAN) assertThat(fingerprint).isEqualTo(fingerprint) } diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordCreationEventTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordCreationEventTest.kt index 96f2f95e93..19f5ca66ce 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordCreationEventTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordCreationEventTest.kt @@ -3,7 +3,7 @@ package com.simprints.infra.eventsync.event.remote.models.subject import com.google.common.truth.Truth.* import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.infra.config.store.remote.models.ApiExternalCredentialType import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordCreationEvent @@ -25,7 +25,7 @@ class ApiEnrolmentRecordCreationEventTest { ApiFingerprintReference( "fpRefId", listOf( - ApiFingerprintTemplate("template", SampleIdentifier.LEFT_THUMB), + ApiFingerprintTemplate("template", TemplateIdentifier.LEFT_THUMB), ), "NEC_1", ), @@ -47,7 +47,7 @@ class ApiEnrolmentRecordCreationEventTest { FingerprintReference( "fpRefId", listOf( - FingerprintTemplate("template", SampleIdentifier.LEFT_THUMB), + FingerprintTemplate("template", TemplateIdentifier.LEFT_THUMB), ), "NEC_1", ), diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordMoveEventTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordMoveEventTest.kt index d4c4cf4de1..d3fbedc78c 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordMoveEventTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordMoveEventTest.kt @@ -3,7 +3,7 @@ package com.simprints.infra.eventsync.event.remote.models.subject import com.google.common.truth.Truth.* import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.infra.config.store.remote.models.ApiExternalCredentialType import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordMoveEvent @@ -26,7 +26,7 @@ class ApiEnrolmentRecordMoveEventTest { ApiFingerprintReference( "fpRefId", listOf( - ApiFingerprintTemplate("template", SampleIdentifier.LEFT_THUMB), + ApiFingerprintTemplate("template", TemplateIdentifier.LEFT_THUMB), ), "NEC_1", ), @@ -54,7 +54,7 @@ class ApiEnrolmentRecordMoveEventTest { FingerprintReference( "fpRefId", listOf( - FingerprintTemplate("template", SampleIdentifier.LEFT_THUMB), + FingerprintTemplate("template", TemplateIdentifier.LEFT_THUMB), ), "NEC_1", ), diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordUpdateEventTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordUpdateEventTest.kt index 27ecfdffce..6c0a8bf210 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordUpdateEventTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/remote/models/subject/ApiEnrolmentRecordUpdateEventTest.kt @@ -3,7 +3,7 @@ package com.simprints.infra.eventsync.event.remote.models.subject import com.google.common.truth.Truth.* import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.infra.config.store.remote.models.ApiExternalCredentialType import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordUpdateEvent @@ -26,7 +26,7 @@ class ApiEnrolmentRecordUpdateEventTest { ApiFingerprintReference( "fpRefId", listOf( - ApiFingerprintTemplate("template", SampleIdentifier.LEFT_THUMB), + ApiFingerprintTemplate("template", TemplateIdentifier.LEFT_THUMB), ), "NEC_1", ), @@ -50,7 +50,7 @@ class ApiEnrolmentRecordUpdateEventTest { biometricReferencesAdded = listOf( FingerprintReference( "fpRefId", - listOf(FingerprintTemplate("template", SampleIdentifier.LEFT_THUMB)), + listOf(FingerprintTemplate("template", TemplateIdentifier.LEFT_THUMB)), "NEC_1", ), FaceReference( diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTaskTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTaskTest.kt index 2a6bee52b7..3f86693b97 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTaskTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/CommCareEventSyncTaskTest.kt @@ -4,7 +4,8 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.time.TimeHelper @@ -12,11 +13,11 @@ import com.simprints.infra.config.store.models.DeviceConfiguration import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Creation -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Deletion -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Update +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Creation +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Deletion +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Update import com.simprints.infra.events.EventRepository import com.simprints.infra.events.event.domain.models.downsync.EventDownSyncRequestEvent import com.simprints.infra.events.event.domain.models.scope.EventScope @@ -38,7 +39,7 @@ import com.simprints.infra.eventsync.status.down.domain.CommCareEventSyncResult import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.COMPLETE import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.FAILED import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.RUNNING -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import com.simprints.infra.eventsync.sync.down.tasks.BaseEventDownSyncTask.Companion.EVENTS_BATCH_SIZE import com.simprints.testtools.common.coroutines.TestCoroutineRule import com.simprints.testtools.unit.EncodingUtilsImplForTests @@ -162,7 +163,7 @@ class CommCareEventSyncTaskTest { @MockK private lateinit var project: Project - private lateinit var subjectFactory: SubjectFactory + private lateinit var enrolmentRecordFactory: EnrolmentRecordFactory @get:Rule val testCoroutineRule = TestCoroutineRule() @@ -171,14 +172,14 @@ class CommCareEventSyncTaskTest { fun setup() { MockKAnnotations.init(this, relaxed = true) - subjectFactory = SubjectFactory( + enrolmentRecordFactory = EnrolmentRecordFactory( encodingUtils = EncodingUtilsImplForTests, timeHelper = timeHelper, ) commCareEventSyncTask = CommCareEventSyncTask( enrolmentRecordRepository, eventDownSyncScopeRepository, - subjectFactory, + enrolmentRecordFactory, configManager, timeHelper, eventRepository, @@ -286,7 +287,7 @@ class CommCareEventSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Creation( - subjectFactory.buildSubjectFromCreationPayload( + enrolmentRecordFactory.buildFromCreationPayload( event.payload, ), ), @@ -327,14 +328,18 @@ class CommCareEventSyncTaskTest { @Test fun downSync_shouldProcessRecordUpdateEvent_withUpdate() = runTest { coEvery { enrolmentRecordRepository.load(any()) } returns listOf( - Subject( + EnrolmentRecord( subjectId = "subjectId", projectId = "projectId", attendantId = "moduleId".asTokenizableRaw(), moduleId = "attendantId".asTokenizableRaw(), - samples = listOf( - Sample( - template = byteArrayOf(), + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(), + ), + ), format = "format", referenceId = "referenceId", modality = Modality.FACE, @@ -350,7 +355,7 @@ class CommCareEventSyncTaskTest { coVerify { enrolmentRecordRepository.performActions( - match> { actions -> + match> { actions -> actions.size == 1 && actions.first() is Update }, any(), @@ -385,7 +390,7 @@ class CommCareEventSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToModule2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), ), project, ) @@ -423,7 +428,7 @@ class CommCareEventSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToModule2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), ), project, ) 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/EnrolmentRecordFactoryTest.kt similarity index 59% rename from infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SubjectFactoryTest.kt rename to infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/EnrolmentRecordFactoryTest.kt index 6a239d4f7a..9955434998 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/EnrolmentRecordFactoryTest.kt @@ -4,15 +4,16 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.CaptureIdentity -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.utils.EncodingUtils -import com.simprints.infra.enrolment.records.repository.domain.models.Subject +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordCreationEvent import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordMoveEvent import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordUpdateEvent.EnrolmentRecordUpdatePayload @@ -21,7 +22,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.infra.events.sampledata.SampleDefaults.GUID1 -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import io.mockk.* import io.mockk.impl.annotations.MockK import org.junit.After @@ -30,7 +31,7 @@ import org.junit.Test import java.util.Date import java.util.UUID -class SubjectFactoryTest { +class EnrolmentRecordFactoryTest { @MockK lateinit var encodingUtils: EncodingUtils @@ -43,7 +44,7 @@ class SubjectFactoryTest { mockkStatic(UUID::class) every { encodingUtils.base64ToBytes(any()) } returns BASE_64_BYTES - factory = SubjectFactory( + factory = EnrolmentRecordFactory( encodingUtils = encodingUtils, timeHelper = timeHelper, ) @@ -55,7 +56,7 @@ class SubjectFactoryTest { } @Test - fun `when buildSubjectFromCreationPayload is called, correct samples are built`() { + fun `when buildFromCreationPayload is called, correct samples are built`() { val payload = EnrolmentRecordCreationEvent.EnrolmentRecordCreationPayload( subjectId = SUBJECT_ID, projectId = PROJECT_ID, @@ -64,22 +65,30 @@ class SubjectFactoryTest { biometricReferences = listOf(FINGERPRINT_REFERENCE, faceReference), externalCredentials = emptyList(), ) - val result = factory.buildSubjectFromCreationPayload(payload) - val expected = Subject( + val result = factory.buildFromCreationPayload(payload) + val expected = EnrolmentRecord( subjectId = SUBJECT_ID, projectId = PROJECT_ID, attendantId = ATTENDANT_ID, moduleId = MODULE_ID, - samples = listOf( - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FINGERPRINT, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FACE, @@ -90,7 +99,7 @@ class SubjectFactoryTest { } @Test - fun `when buildSubjectFromMovePayload is called, correct samples are built`() { + fun `when buildFromMovePayload is called, correct samples are built`() { val payload = EnrolmentRecordMoveEvent.EnrolmentRecordCreationInMove( subjectId = SUBJECT_ID, projectId = PROJECT_ID, @@ -99,23 +108,31 @@ class SubjectFactoryTest { biometricReferences = listOf(FINGERPRINT_REFERENCE, faceReference), externalCredential = null, ) - val result = factory.buildSubjectFromMovePayload(payload) + val result = factory.buildFromMovePayload(payload) - val expected = Subject( + val expected = EnrolmentRecord( subjectId = SUBJECT_ID, projectId = PROJECT_ID, attendantId = ATTENDANT_ID, moduleId = MODULE_ID, - samples = listOf( - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FINGERPRINT, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FACE, @@ -126,35 +143,51 @@ class SubjectFactoryTest { } @Test - fun `when buildSubjectFromUpdatePayload is called, correct samples list is created`() { - val subject = Subject( + fun `when buildFromUpdatePayload is called, correct samples list is created`() { + val enrolmentRecord = EnrolmentRecord( subjectId = SUBJECT_ID, projectId = PROJECT_ID, attendantId = ATTENDANT_ID, moduleId = MODULE_ID, - samples = listOf( - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-1", modality = Modality.FINGERPRINT, ), - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-2", modality = Modality.FINGERPRINT, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-3", modality = Modality.FACE, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-4", modality = Modality.FACE, @@ -173,7 +206,7 @@ class SubjectFactoryTest { templates = listOf( FingerprintTemplate( template = BASE_64_BYTES.toString(), - finger = SampleIdentifier.LEFT_THUMB, + finger = TemplateIdentifier.LEFT_THUMB, ), ), ), @@ -185,36 +218,52 @@ class SubjectFactoryTest { ), externalCredentialsAdded = listOf(EXTERNAL_CREDENTIAL), ) - val result = factory.buildSubjectFromUpdatePayload(subject, payload) + val result = factory.buildFromUpdatePayload(enrolmentRecord, payload) - val expected = Subject( + val expected = EnrolmentRecord( subjectId = SUBJECT_ID, projectId = PROJECT_ID, attendantId = ATTENDANT_ID, moduleId = MODULE_ID, - samples = listOf( - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-1", modality = Modality.FINGERPRINT, ), - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-5", modality = Modality.FINGERPRINT, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-4", modality = Modality.FACE, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = "referenceId-finger-6", modality = Modality.FACE, @@ -223,30 +272,41 @@ class SubjectFactoryTest { externalCredentials = listOf(EXTERNAL_CREDENTIAL), ) assertThat(result.subjectId).isEqualTo(expected.subjectId) - assertThat(result.samples.size).isEqualTo(expected.samples.size) - assertThat(result.samples).containsExactlyElementsIn(expected.samples) + assertThat(result.references.size).isEqualTo(expected.references.size) + assertThat(result.references).containsExactlyElementsIn(expected.references) } @Test - fun `when buildSubjectFromCaptureResults is called, correct subject is built`() { - every { UUID.randomUUID().toString() } returns SUBJECT_ID + fun `when buildFromCaptureResults is called, correct subject is built`() { + val randomUUID = "5a95b24d-23c5-4d24-9277-0d3d2a287508" // "chosen by fair dice roll. guaranteed to be random" (c) xkcd + every { UUID.randomUUID().toString() } returns randomUUID - val expected = Subject( - subjectId = SUBJECT_ID, + val expected = EnrolmentRecord( + subjectId = randomUUID, projectId = PROJECT_ID, attendantId = ATTENDANT_ID, moduleId = MODULE_ID, createdAt = Date(0L), - samples = listOf( - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + id = randomUUID, + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FINGERPRINT, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + id = randomUUID, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FACE, @@ -255,34 +315,32 @@ class SubjectFactoryTest { externalCredentials = listOf(EXTERNAL_CREDENTIAL), ) - val result = factory.buildSubjectFromCaptureResults( + val result = factory.buildFromCaptureResults( subjectId = expected.subjectId, projectId = expected.projectId, attendantId = expected.attendantId, moduleId = expected.moduleId, captures = listOf( - CaptureIdentity( - GUID1, - Modality.FINGERPRINT, - listOf( - CaptureSample( + BiometricReferenceCapture( + referenceId = REFERENCE_ID, + modality = Modality.FINGERPRINT, + format = REFERENCE_FORMAT, + templates = listOf( + BiometricTemplateCapture( captureEventId = GUID1, identifier = IDENTIFIER, template = BASE_64_BYTES, - format = REFERENCE_FORMAT, - modality = Modality.FINGERPRINT, ), ), ), - CaptureIdentity( - GUID1, - Modality.FACE, - listOf( - CaptureSample( + BiometricReferenceCapture( + referenceId = REFERENCE_ID, + modality = Modality.FACE, + format = REFERENCE_FORMAT, + templates = listOf( + BiometricTemplateCapture( captureEventId = GUID1, template = BASE_64_BYTES, - format = REFERENCE_FORMAT, - modality = Modality.FACE, ), ), ), @@ -293,22 +351,30 @@ class SubjectFactoryTest { } @Test - fun `when buildSubject is called, correct subject is built`() { - val expected = Subject( + fun `when buildEnrolmentRecord is called, correct subject is built`() { + val expected = EnrolmentRecord( subjectId = SUBJECT_ID, projectId = PROJECT_ID, attendantId = ATTENDANT_ID, moduleId = MODULE_ID, - samples = listOf( - Sample( - identifier = IDENTIFIER, - template = BASE_64_BYTES, + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = IDENTIFIER, + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FINGERPRINT, ), - Sample( - template = BASE_64_BYTES, + BiometricReference( + templates = listOf( + BiometricTemplate( + template = BASE_64_BYTES, + ), + ), format = REFERENCE_FORMAT, referenceId = REFERENCE_ID, modality = Modality.FACE, @@ -317,19 +383,19 @@ class SubjectFactoryTest { externalCredentials = listOf(EXTERNAL_CREDENTIAL), ) - val result = factory.buildSubject( + val result = factory.buildEnrolmentRecord( subjectId = expected.subjectId, projectId = expected.projectId, attendantId = expected.attendantId, moduleId = expected.moduleId, - samples = expected.samples, + references = expected.references, externalCredentials = expected.externalCredentials, ) assertThat(result).isEqualTo(expected) } companion object { - private lateinit var factory: SubjectFactory + private lateinit var factory: EnrolmentRecordFactory private const val PROJECT_ID = "projectId" private const val SUBJECT_ID = "subjectId" private const val EXTERNAL_CREDENTIAL_ID = "credentialId" @@ -341,7 +407,7 @@ class SubjectFactoryTest { private const val TEMPLATE_NAME = "template" private val EXTERNAL_CREDENTIAL_VALUE = "value".asTokenizableEncrypted() private val EXTERNAL_CREDENTIAL_TYPE = ExternalCredentialType.NHISCard - private val IDENTIFIER = SampleIdentifier.LEFT_THUMB + private val IDENTIFIER = TemplateIdentifier.LEFT_THUMB private val FINGERPRINT_REFERENCE = FingerprintReference( id = REFERENCE_ID, format = REFERENCE_FORMAT, diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTaskTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTaskTest.kt index 20da78d3ce..f68265eda6 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTaskTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/sync/down/tasks/SimprintsEventDownSyncTaskTest.kt @@ -4,7 +4,8 @@ import com.google.common.truth.Truth.* import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.domain.tokenization.asTokenizableRaw import com.simprints.core.tools.time.TimeHelper @@ -13,11 +14,11 @@ import com.simprints.infra.config.store.models.DeviceConfiguration import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Creation -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Deletion -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction.Update +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Creation +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Deletion +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction.Update import com.simprints.infra.events.EventRepository import com.simprints.infra.events.event.domain.models.downsync.EventDownSyncRequestEvent import com.simprints.infra.events.event.domain.models.scope.EventScope @@ -39,7 +40,7 @@ import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.D import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.FAILED import com.simprints.infra.eventsync.status.down.domain.EventDownSyncOperation.DownSyncState.RUNNING import com.simprints.infra.eventsync.status.down.domain.EventDownSyncResult -import com.simprints.infra.eventsync.sync.common.SubjectFactory +import com.simprints.infra.eventsync.sync.common.EnrolmentRecordFactory import com.simprints.infra.eventsync.sync.down.tasks.BaseEventDownSyncTask.Companion.EVENTS_BATCH_SIZE import com.simprints.testtools.common.coroutines.TestCoroutineRule import com.simprints.testtools.unit.EncodingUtilsImplForTests @@ -187,7 +188,7 @@ class SimprintsEventDownSyncTaskTest { @MockK private lateinit var project: Project - private lateinit var subjectFactory: SubjectFactory + private lateinit var enrolmentRecordFactory: EnrolmentRecordFactory @get:Rule val testCoroutineRule = TestCoroutineRule() @@ -196,14 +197,14 @@ class SimprintsEventDownSyncTaskTest { fun setup() { MockKAnnotations.init(this, relaxed = true) - subjectFactory = SubjectFactory( + enrolmentRecordFactory = EnrolmentRecordFactory( encodingUtils = EncodingUtilsImplForTests, timeHelper = timeHelper, ) eventDownSyncTask = SimprintsEventDownSyncTask( enrolmentRecordRepository, eventDownSyncScopeRepository, - subjectFactory, + enrolmentRecordFactory, configManager, timeHelper, eventRepository, @@ -305,7 +306,7 @@ class SimprintsEventDownSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Creation( - subjectFactory.buildSubjectFromCreationPayload( + enrolmentRecordFactory.buildFromCreationPayload( event.payload, ), ), @@ -383,7 +384,7 @@ class SimprintsEventDownSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToModule2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), ), project, ) @@ -411,7 +412,7 @@ class SimprintsEventDownSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToModule2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), ), project, ) @@ -461,7 +462,7 @@ class SimprintsEventDownSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToModule2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), ), project, ) @@ -489,7 +490,7 @@ class SimprintsEventDownSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToModule2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), ), project, ) @@ -522,7 +523,7 @@ class SimprintsEventDownSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToAttendant2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToAttendant2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToAttendant2.payload.enrolmentRecordCreation)), ), project, ) @@ -540,7 +541,7 @@ class SimprintsEventDownSyncTaskTest { enrolmentRecordRepository.performActions( listOf( Deletion(eventToMoveToModule2.payload.enrolmentRecordDeletion.subjectId), - Creation(subjectFactory.buildSubjectFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), + Creation(enrolmentRecordFactory.buildFromMovePayload(eventToMoveToModule2.payload.enrolmentRecordCreation)), ), project, ) @@ -550,14 +551,18 @@ class SimprintsEventDownSyncTaskTest { @Test fun downSync_shouldProcessRecordUpdateEvent_withUpdate() = runTest { coEvery { enrolmentRecordRepository.load(any()) } returns listOf( - Subject( + EnrolmentRecord( subjectId = "subjectId", projectId = "projectId", attendantId = "moduleId".asTokenizableRaw(), moduleId = "attendantId".asTokenizableRaw(), - samples = listOf( - Sample( - template = byteArrayOf(), + references = listOf( + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(), + ), + ), format = "format", referenceId = "referenceId", modality = Modality.FACE, @@ -581,7 +586,7 @@ class SimprintsEventDownSyncTaskTest { coVerify { enrolmentRecordRepository.performActions( - match> { actions -> + match> { actions -> actions.size == 1 && actions.first() is Update }, any(), diff --git a/infra/events/src/debug/java/com/simprints/infra/events/sampledata/EventFactoryUtils.kt b/infra/events/src/debug/java/com/simprints/infra/events/sampledata/EventFactoryUtils.kt index 2937ed8ac2..7498072017 100644 --- a/infra/events/src/debug/java/com/simprints/infra/events/sampledata/EventFactoryUtils.kt +++ b/infra/events/src/debug/java/com/simprints/infra/events/sampledata/EventFactoryUtils.kt @@ -2,8 +2,8 @@ package com.simprints.infra.events.sampledata import android.os.Build import com.simprints.core.domain.common.Modality +import com.simprints.core.domain.common.TemplateIdentifier.LEFT_THUMB import com.simprints.core.domain.response.AppMatchConfidence.MEDIUM -import com.simprints.core.domain.sample.SampleIdentifier.LEFT_THUMB import com.simprints.core.tools.time.Timestamp import com.simprints.core.tools.utils.SimNetworkUtils import com.simprints.core.tools.utils.SimNetworkUtils.Connection diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt index fdb51b9bc7..7dd003569b 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt @@ -1,7 +1,7 @@ package com.simprints.infra.events.event.domain.models.fingerprint import androidx.annotation.Keep -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.tools.time.Timestamp import com.simprints.core.tools.utils.randomUUID @@ -51,7 +51,7 @@ data class FingerprintCaptureBiometricsEvent( @Keep data class Fingerprint( - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val template: String, val quality: Int, val format: String, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt index 14da649a0c..df4a1f786f 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt @@ -1,7 +1,7 @@ package com.simprints.infra.events.event.domain.models.fingerprint import androidx.annotation.Keep -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.tools.time.Timestamp import com.simprints.core.tools.utils.randomUUID @@ -22,7 +22,7 @@ data class FingerprintCaptureEvent( constructor( createdAt: Timestamp, endTime: Timestamp, - finger: SampleIdentifier, + finger: TemplateIdentifier, qualityThreshold: Int, result: FingerprintCapturePayload.Result, fingerprint: FingerprintCapturePayload.Fingerprint?, @@ -52,7 +52,7 @@ data class FingerprintCaptureEvent( override val createdAt: Timestamp, override val eventVersion: Int, override var endedAt: Timestamp?, - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val qualityThreshold: Int, val result: Result, val fingerprint: Fingerprint?, @@ -64,7 +64,7 @@ data class FingerprintCaptureEvent( @Keep data class Fingerprint( - val finger: SampleIdentifier, + val finger: TemplateIdentifier, val quality: Int, val format: String, ) diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/EnrolmentRecordCreationEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/EnrolmentRecordCreationEvent.kt index 064295b2f7..16edb9647f 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/EnrolmentRecordCreationEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/EnrolmentRecordCreationEvent.kt @@ -4,10 +4,10 @@ import androidx.annotation.Keep import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredential -import com.simprints.core.domain.sample.Sample import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.tools.utils.EncodingUtils import java.util.UUID +import com.simprints.core.domain.reference.BiometricReference as CoreBiometricReference @Keep @ExcludedFromGeneratedTestCoverageReports("Data class") @@ -46,13 +46,13 @@ data class EnrolmentRecordCreationEvent( companion object { fun buildBiometricReferences( - samples: List, + references: List, encoder: EncodingUtils, - ): List = samples.groupBy { it.modality }.mapNotNull { (modality, modalitySamples) -> - if (modalitySamples.isNotEmpty()) { - when (modality) { - Modality.FACE -> buildFingerprintReference(modalitySamples, encoder) - Modality.FINGERPRINT -> buildFaceReference(modalitySamples, encoder) + ): List = references.mapNotNull { reference -> + if (reference.templates.isNotEmpty()) { + when (reference.modality) { + Modality.FACE -> buildFaceReference(reference, encoder) + Modality.FINGERPRINT -> buildFingerprintReference(reference, encoder) } } else { null @@ -60,30 +60,30 @@ data class EnrolmentRecordCreationEvent( } private fun buildFingerprintReference( - fingerprintSamples: List, + reference: CoreBiometricReference, encoder: EncodingUtils, ) = FingerprintReference( - fingerprintSamples.first().referenceId, - fingerprintSamples.map { + id = reference.referenceId, + templates = reference.templates.map { FingerprintTemplate( - encoder.byteArrayToBase64(it.template), - it.identifier, + template = encoder.byteArrayToBase64(it.template), + finger = it.identifier, ) }, - fingerprintSamples.first().format, + format = reference.format, ) private fun buildFaceReference( - faceSamples: List, + reference: CoreBiometricReference, encoder: EncodingUtils, ) = FaceReference( - faceSamples.first().referenceId, - faceSamples.map { + id = reference.referenceId, + templates = reference.templates.map { FaceTemplate( - encoder.byteArrayToBase64(it.template), + template = encoder.byteArrayToBase64(it.template), ) }, - faceSamples.first().format, + format = reference.format, ) } } diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/FingerprintTemplate.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/FingerprintTemplate.kt index 8da5a26d52..d795c237ca 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/FingerprintTemplate.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/subject/FingerprintTemplate.kt @@ -1,10 +1,10 @@ package com.simprints.infra.events.event.domain.models.subject import androidx.annotation.Keep -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier @Keep data class FingerprintTemplate( val template: String, - val finger: SampleIdentifier, + val finger: TemplateIdentifier, ) diff --git a/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/EventPayloadTest.kt b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/EventPayloadTest.kt index df340a167e..433e628596 100644 --- a/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/EventPayloadTest.kt +++ b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/EventPayloadTest.kt @@ -1,8 +1,8 @@ package com.simprints.infra.events.event.domain.models import com.google.common.truth.Truth.assertThat +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.response.AppMatchConfidence -import com.simprints.core.domain.sample.SampleIdentifier import com.simprints.core.tools.utils.SimNetworkUtils import com.simprints.core.tools.utils.SimNetworkUtils.Connection import com.simprints.infra.events.event.domain.models.AlertScreenEvent.AlertScreenPayload.AlertScreenEventType.BLUETOOTH_NOT_ENABLED @@ -160,7 +160,7 @@ class EventPayloadTest { FingerprintCaptureBiometricsEvent( createdAt = CREATED_AT, fingerprint = FingerprintCaptureBiometricsEvent.FingerprintCaptureBiometricsPayload.Fingerprint( - finger = SampleIdentifier.LEFT_3RD_FINGER, + finger = TemplateIdentifier.LEFT_3RD_FINGER, template = "template", quality = 1, format = "ISO_19794_2", @@ -170,11 +170,11 @@ class EventPayloadTest { FingerprintCaptureEvent( createdAt = CREATED_AT, endTime = ENDED_AT, - finger = SampleIdentifier.LEFT_THUMB, + finger = TemplateIdentifier.LEFT_THUMB, qualityThreshold = 10, result = FingerprintCaptureEvent.FingerprintCapturePayload.Result.BAD_QUALITY, fingerprint = FingerprintCaptureEvent.FingerprintCapturePayload.Fingerprint( - finger = SampleIdentifier.LEFT_THUMB, + finger = TemplateIdentifier.LEFT_THUMB, quality = 8, format = "ISO_19794_2", ), diff --git a/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEventTest.kt b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEventTest.kt index ba3b57f002..425fbf6af0 100644 --- a/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEventTest.kt +++ b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEventTest.kt @@ -1,7 +1,7 @@ package com.simprints.infra.events.event.domain.models.fingerprint import com.google.common.truth.Truth.assertThat -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.events.event.domain.models.EventType import com.simprints.infra.events.sampledata.SampleDefaults import org.junit.Test @@ -11,7 +11,7 @@ class FingerprintCaptureBiometricsEventTest { fun create_FingerprintCaptureBiometricsEvent() { val fingerArg = FingerprintCaptureBiometricsEvent.FingerprintCaptureBiometricsPayload.Fingerprint( - SampleIdentifier.LEFT_3RD_FINGER, + TemplateIdentifier.LEFT_3RD_FINGER, "template", 1, "ISO_19794_2", diff --git a/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEventTest.kt b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEventTest.kt index 742a9adf60..a6d370d926 100644 --- a/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEventTest.kt +++ b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEventTest.kt @@ -1,7 +1,7 @@ package com.simprints.infra.events.event.domain.models.fingerprint import com.google.common.truth.Truth.assertThat -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.infra.events.event.domain.models.EventType import com.simprints.infra.events.sampledata.SampleDefaults import org.junit.Test @@ -10,14 +10,14 @@ class FingerprintCaptureEventTest { @Test fun create_FingerprintCaptureEvent() { val fingerprint = FingerprintCaptureEvent.FingerprintCapturePayload.Fingerprint( - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, 8, "ISO_19794_2", ) val event = FingerprintCaptureEvent( SampleDefaults.CREATED_AT, SampleDefaults.ENDED_AT, - SampleIdentifier.LEFT_THUMB, + TemplateIdentifier.LEFT_THUMB, 10, FingerprintCaptureEvent.FingerprintCapturePayload.Result.BAD_QUALITY, fingerprint, @@ -30,7 +30,7 @@ class FingerprintCaptureEventTest { assertThat(endedAt).isEqualTo(SampleDefaults.ENDED_AT) assertThat(eventVersion).isEqualTo(FingerprintCaptureEvent.EVENT_VERSION) assertThat(type).isEqualTo(EventType.FINGERPRINT_CAPTURE) - assertThat(finger).isEqualTo(SampleIdentifier.LEFT_THUMB) + assertThat(finger).isEqualTo(TemplateIdentifier.LEFT_THUMB) assertThat(qualityThreshold).isEqualTo(10) assertThat(fingerprint).isEqualTo(fingerprint) } diff --git a/infra/events/src/test/java/com/simprints/infra/events/event/local/migrations/EventMigration7To8Test.kt b/infra/events/src/test/java/com/simprints/infra/events/event/local/migrations/EventMigration7To8Test.kt index 0763185a86..2e19bcf697 100644 --- a/infra/events/src/test/java/com/simprints/infra/events/event/local/migrations/EventMigration7To8Test.kt +++ b/infra/events/src/test/java/com/simprints/infra/events/event/local/migrations/EventMigration7To8Test.kt @@ -9,7 +9,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.google.common.truth.Truth.assertThat -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.tools.extentions.getStringWithColumnName import com.simprints.core.tools.utils.randomUUID import com.simprints.infra.events.event.local.EventRoomDatabase @@ -475,7 +475,7 @@ class EventMigration7To8Test { private const val SESSION_ID = "aSessionID" private const val DEVICE_ID = "aDeviceID" private const val TEMPLATE = "template" - private val FINGER = SampleIdentifier.LEFT_3RD_FINGER + private val FINGER = TemplateIdentifier.LEFT_3RD_FINGER private val CREATED_AT = 1611584017198 private val ENDED_AT = 1621588617198 private const val QUALITY = 85 diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/MatchParams.kt b/infra/matching/src/main/java/com/simprints/infra/matching/MatchParams.kt index b22dfd329d..6ccba0cbaa 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/MatchParams.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/MatchParams.kt @@ -3,18 +3,16 @@ package com.simprints.infra.matching import androidx.annotation.Keep import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.ModalitySdkType -import com.simprints.core.domain.sample.CaptureSample +import com.simprints.core.domain.capture.BiometricReferenceCapture import com.simprints.core.domain.step.StepParams -import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery @Keep data class MatchParams( - val probeReferenceId: String, val bioSdk: ModalitySdkType, - val probeSamples: List = emptyList(), + val probeReference: BiometricReferenceCapture, val flowType: FlowType, - val queryForCandidates: SubjectQuery, + val queryForCandidates: EnrolmentRecordQuery, val biometricDataSource: BiometricDataSource, ) : StepParams diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/MatchResult.kt b/infra/matching/src/main/java/com/simprints/infra/matching/MatchResult.kt index 755ce5634d..bf95f73ce1 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/MatchResult.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/MatchResult.kt @@ -2,11 +2,11 @@ package com.simprints.infra.matching import androidx.annotation.Keep import com.simprints.core.domain.common.ModalitySdkType -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.domain.step.StepResult @Keep data class MatchResult( - val results: List, + val results: List, val sdk: ModalitySdkType, ) : StepResult diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt index c0ee511cea..e45d565832 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt @@ -1,9 +1,9 @@ package com.simprints.infra.matching.usecase import com.simprints.core.DispatcherBG -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.tools.time.TimeHelper import com.simprints.face.infra.basebiosdk.matching.FaceMatcher import com.simprints.face.infra.biosdkresolver.FaceBioSDK @@ -11,7 +11,7 @@ import com.simprints.face.infra.biosdkresolver.ResolveFaceBioSdkUseCase import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch import com.simprints.infra.logging.LoggingConstants import com.simprints.infra.logging.Simber import com.simprints.infra.matching.MatchBatchInfo @@ -49,11 +49,11 @@ class FaceMatcherUseCase @Inject constructor( return@channelFlow } val bioSdk = resolveFaceBioSdk(matchParams.bioSdk) - - if (matchParams.probeSamples.isEmpty()) { + if (matchParams.probeReference.templates.isEmpty()) { send(MatcherState.Success(emptyList(), emptyList(), 0, bioSdk.matcherName())) return@channelFlow } + val queryWithSupportedFormat = matchParams.queryForCandidates.copy( format = bioSdk.templateFormat(), ) @@ -76,7 +76,7 @@ class FaceMatcherUseCase @Inject constructor( val ranges = createRanges(expectedCandidates) val resultSet = MatchResultSet() val candidatesChannel = enrolmentRecordRepository - .loadIdentities( + .loadCandidateRecords( query = queryWithSupportedFormat, ranges = ranges, dataSource = matchParams.biometricDataSource, @@ -87,20 +87,20 @@ class FaceMatcherUseCase @Inject constructor( this@channelFlow.send(MatcherState.CandidateLoaded) } - val batchInfo = consumeAndMatch(candidatesChannel, matchParams.probeSamples, resultSet, bioSdk) + val batchInfo = consumeAndMatch(candidatesChannel, matchParams.probeReference, resultSet, bioSdk) send(MatcherState.Success(resultSet.toList(), batchInfo, loadedCandidates.get(), bioSdk.matcherName())) }.flowOn(dispatcherBG) private suspend fun consumeAndMatch( - candidatesChannel: ReceiveChannel, - samples: List, + candidatesChannel: ReceiveChannel, + probeReference: BiometricReferenceCapture, resultSet: MatchResultSet, bioSdk: FaceBioSDK, ): List { val matchBatches = mutableListOf() for (batch in candidatesChannel) { val comparingStartTime = timeHelper.now() - val results = bioSdk.createMatcher(samples).use { matcher -> + val results = bioSdk.createMatcher(probeReference).use { matcher -> match(matcher, batch.identities) } resultSet.addAll(results) @@ -120,10 +120,10 @@ class FaceMatcherUseCase @Inject constructor( private suspend fun match( matcher: FaceMatcher, - batchCandidates: List, + batchCandidates: List, ) = batchCandidates.fold(MatchResultSet()) { acc, candidate -> acc.add( - MatchComparisonResult( + ComparisonResult( candidate.subjectId, matcher.getHighestComparisonScoreForCandidate(candidate), ), diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt index 9fdd14e0f3..569f39a6fb 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt @@ -2,8 +2,8 @@ package com.simprints.infra.matching.usecase import com.simprints.core.DispatcherBG import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.tools.time.TimeHelper import com.simprints.fingerprint.infra.biosdk.BioSdkWrapper import com.simprints.fingerprint.infra.biosdk.ResolveBioSdkWrapperUseCase @@ -12,7 +12,7 @@ import com.simprints.infra.config.store.models.FingerprintConfiguration.FingerCo import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch import com.simprints.infra.logging.LoggingConstants import com.simprints.infra.logging.Simber import com.simprints.infra.matching.MatchBatchInfo @@ -51,11 +51,11 @@ class FingerprintMatcherUseCase @Inject constructor( return@channelFlow } val bioSdkWrapper = resolveBioSdkWrapper(matchParams.bioSdk) - - if (matchParams.probeSamples.isEmpty()) { + if (matchParams.probeReference.templates.isEmpty()) { send(MatcherState.Success(emptyList(), emptyList(), 0, bioSdkWrapper.matcherName)) return@channelFlow } + // Only candidates with supported template format are considered val queryWithSupportedFormat = matchParams.queryForCandidates.copy( @@ -76,7 +76,7 @@ class FingerprintMatcherUseCase @Inject constructor( val loadedCandidates = AtomicInteger(0) val ranges = createRanges(expectedCandidates) // if number of ranges less than the number of cores then use the number of ranges - val channel = enrolmentRecordRepository.loadIdentities( + val channel = enrolmentRecordRepository.loadCandidateRecords( query = queryWithSupportedFormat, ranges = ranges, dataSource = matchParams.biometricDataSource, @@ -91,7 +91,7 @@ class FingerprintMatcherUseCase @Inject constructor( val batchInfo = consumeAndMatch( channel = channel, - samples = matchParams.probeSamples, + probeReference = matchParams.probeReference, resultSet = resultSet, bioSdk = matchParams.bioSdk, bioSdkWrapper = bioSdkWrapper, @@ -103,8 +103,8 @@ class FingerprintMatcherUseCase @Inject constructor( }.flowOn(dispatcherBG) private suspend fun consumeAndMatch( - channel: ReceiveChannel, - samples: List, + channel: ReceiveChannel, + probeReference: BiometricReferenceCapture, resultSet: MatchResultSet, bioSdk: FingerprintConfiguration.BioSdk, bioSdkWrapper: BioSdkWrapper, @@ -115,7 +115,7 @@ class FingerprintMatcherUseCase @Inject constructor( val comparingStartTime = timeHelper.now() val matchResults = match( - probes = samples, + probeReference = probeReference, candidates = batch.identities, flowType = flowType, bioSdkWrapper = bioSdkWrapper, @@ -137,13 +137,13 @@ class FingerprintMatcherUseCase @Inject constructor( } private suspend fun match( - probes: List, - candidates: List, + probeReference: BiometricReferenceCapture, + candidates: List, flowType: FlowType, bioSdkWrapper: BioSdkWrapper, bioSdk: FingerprintConfiguration.BioSdk, ) = bioSdkWrapper.match( - probes, + probeReference, candidates, isCrossFingerMatchingEnabled(flowType, bioSdk), ) diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatchResultSet.kt b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatchResultSet.kt index cd9b711347..396f0fd94c 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatchResultSet.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatchResultSet.kt @@ -1,6 +1,6 @@ package com.simprints.infra.matching.usecase -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import java.util.concurrent.ConcurrentSkipListSet import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.locks.ReentrantLock @@ -13,14 +13,14 @@ internal class MatchResultSet( private val lock = ReentrantLock() private val skipListSet = ConcurrentSkipListSet( - compareByDescending { it.confidence }.thenByDescending { it.subjectId }, + compareByDescending { it.comparisonScore }.thenByDescending { it.subjectId }, ) - fun add(element: MatchComparisonResult): MatchResultSet { + fun add(element: ComparisonResult): MatchResultSet { // Use a lock to ensure thread safety during the entire add operation lock.withLock { // Only perform this optimization when we know the set is at max capacity - if (skipListSet.size >= maxSize && lowestConfidence.get() > element.confidence) { + if (skipListSet.size >= maxSize && lowestConfidence.get() > element.comparisonScore) { // skip adding if the set is full and the last element has higher confidence than the current element return this } @@ -31,7 +31,7 @@ internal class MatchResultSet( // Now that the set is full, we can skip adding elements // with confidence lower than the current lowest - lowestConfidence.set(skipListSet.last().confidence) + lowestConfidence.set(skipListSet.last().comparisonScore) } return this } @@ -42,7 +42,7 @@ internal class MatchResultSet( return this } - fun toList(): List = skipListSet.toList() + fun toList(): List = skipListSet.toList() companion object { /** diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatcherUseCase.kt b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatcherUseCase.kt index 8fde808145..2c4041964c 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatcherUseCase.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/MatcherUseCase.kt @@ -1,6 +1,6 @@ package com.simprints.infra.matching.usecase -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.infra.config.store.models.Project import com.simprints.infra.logging.LoggingConstants import com.simprints.infra.matching.MatchBatchInfo @@ -23,7 +23,7 @@ interface MatcherUseCase { data object CandidateLoaded : MatcherState() data class Success( - val comparisonResults: List, + val comparisonResults: List, val matchBatches: List, val totalCandidates: Int, val matcherName: String, diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCase.kt b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCase.kt index a59b2ca6f5..4072551ace 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCase.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCase.kt @@ -2,11 +2,11 @@ package com.simprints.infra.matching.usecase import com.simprints.core.SessionCoroutineScope import com.simprints.core.domain.common.FlowType -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.tools.time.Timestamp import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.sync.ConfigManager -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.event.domain.models.FingerComparisonStrategy import com.simprints.infra.events.event.domain.models.MatchEntry import com.simprints.infra.events.event.domain.models.OneToManyMatchEvent @@ -30,11 +30,11 @@ class SaveMatchEventUseCase @Inject constructor( matchParams: MatchParams, candidatesCount: Int, matcherName: String, - results: List, + results: List, batches: List, ) { sessionCoroutineScope.launch { - val matchEntries = results.map { MatchEntry(it.subjectId, it.confidence) } + val matchEntries = results.map { MatchEntry(it.subjectId, it.comparisonScore) } val event = if (matchParams.flowType == FlowType.VERIFY) { getOneToOneEvent( startTime, @@ -45,7 +45,7 @@ class SaveMatchEventUseCase @Inject constructor( matchParams.bioSdk .let { it as? FingerprintConfiguration.BioSdk } ?.let { getFingerprintComparisonStrategy(it) }, - matchParams.probeReferenceId, + matchParams.probeReference.referenceId, ) } else { getOneToManyEvent( @@ -55,7 +55,7 @@ class SaveMatchEventUseCase @Inject constructor( matchParams.queryForCandidates, candidatesCount, matchEntries, - matchParams.probeReferenceId, + matchParams.probeReference.referenceId, batches, ) } @@ -79,7 +79,7 @@ class SaveMatchEventUseCase @Inject constructor( startTime: Timestamp, endTime: Timestamp, matcherName: String, - queryForCandidates: SubjectQuery, + queryForCandidates: EnrolmentRecordQuery, matchEntry: MatchEntry?, fingerComparisonStrategy: FingerComparisonStrategy?, biometricReferenceId: String, @@ -97,7 +97,7 @@ class SaveMatchEventUseCase @Inject constructor( startTime: Timestamp, endTime: Timestamp, matcherName: String, - queryForCandidates: SubjectQuery, + queryForCandidates: EnrolmentRecordQuery, candidatesCount: Int, matchEntries: List, biometricReferenceId: String, @@ -123,7 +123,7 @@ class SaveMatchEventUseCase @Inject constructor( }, ) - private fun SubjectQuery.parseQueryAsCoreMatchPoolType(): OneToManyMatchEvent.OneToManyMatchPayload.MatchPoolType = when { + private fun EnrolmentRecordQuery.parseQueryAsCoreMatchPoolType(): OneToManyMatchEvent.OneToManyMatchPayload.MatchPoolType = when { this.attendantId != null -> OneToManyMatchEvent.OneToManyMatchPayload.MatchPoolType.USER this.moduleId != null -> OneToManyMatchEvent.OneToManyMatchPayload.MatchPoolType.MODULE else -> OneToManyMatchEvent.OneToManyMatchPayload.MatchPoolType.PROJECT diff --git a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt index e0ade2049a..94b3bf5003 100644 --- a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt +++ b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt @@ -2,12 +2,14 @@ package com.simprints.infra.matching.usecase import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.google.common.truth.Truth.* +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.MatchComparisonResult -import com.simprints.core.domain.sample.Sample +import com.simprints.core.domain.comparison.ComparisonResult +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.reference.CandidateRecord import com.simprints.core.tools.time.TimeHelper import com.simprints.face.infra.basebiosdk.matching.FaceMatcher import com.simprints.face.infra.biosdkresolver.ResolveFaceBioSdkUseCase @@ -16,7 +18,7 @@ import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.logging.LoggingConstants import com.simprints.infra.logging.Simber import com.simprints.infra.matching.MatchParams @@ -76,11 +78,16 @@ internal class FaceMatcherUseCaseTest { val results = useCase .invoke( MatchParams( - probeReferenceId = "referenceId", flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, biometricDataSource = BiometricDataSource.Simprints, + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "format", + templates = emptyList(), + ), ), project, ).toList() @@ -104,18 +111,20 @@ internal class FaceMatcherUseCaseTest { val results = useCase .invoke( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf( - CaptureSample( - captureEventId = "faceId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FACE, - format = "format", + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "faceId", + template = byteArrayOf(1, 2, 3), + ), ), ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), project, @@ -141,18 +150,20 @@ internal class FaceMatcherUseCaseTest { val results = useCase .invoke( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf( - CaptureSample( - captureEventId = "faceId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FACE, - format = "format", + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "faceId", + template = byteArrayOf(1, 2, 3), + ), ), ), bioSdk = FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, // Wrong SDK type flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), project, @@ -182,12 +193,16 @@ internal class FaceMatcherUseCaseTest { @Test fun `Correctly calls SDK matcher`() = runTest { val totalCandidates = 1 - val faceIdentities = listOf( - Identity( + val faceCandidates = listOf( + CandidateRecord( "subjectId", listOf( - Sample( - template = byteArrayOf(1, 2, 3), + BiometricReference( + templates = listOf( + BiometricTemplate( + template = byteArrayOf(1, 2, 3), + ), + ), format = "format", referenceId = "faceTemplate", modality = Modality.FACE, @@ -198,7 +213,7 @@ internal class FaceMatcherUseCaseTest { coEvery { enrolmentRecordRepository.count(any(), any()) } returns 1 coEvery { createRangesUseCase(any()) } returns listOf(0..99) coEvery { - enrolmentRecordRepository.loadIdentities(any(), any(), any(), any(), any(), any()) + enrolmentRecordRepository.loadCandidateRecords(any(), any(), any(), any(), any(), any()) } answers { // Call the onCandidateLoaded callback (5th parameter) val onCandidateLoaded: suspend () -> Unit = arg(5) @@ -207,25 +222,27 @@ internal class FaceMatcherUseCaseTest { } // Return the face identities - createTestChannel(faceIdentities) + createTestChannel(faceCandidates) } coEvery { faceMatcher.getHighestComparisonScoreForCandidate(any()) } returns 42f val results = useCase .invoke( matchParams = MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf( - CaptureSample( - captureEventId = "faceId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FACE, - format = "format", + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "faceId", + template = byteArrayOf(1, 2, 3), + ), ), ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), project, @@ -239,12 +256,12 @@ internal class FaceMatcherUseCaseTest { assertThat(results[1]).isInstanceOf(MatcherUseCase.MatcherState.CandidateLoaded::class.java) val successState = results[2] as MatcherUseCase.MatcherState.Success - assertThat(successState.comparisonResults).containsExactly(MatchComparisonResult("subjectId", 42f)) + assertThat(successState.comparisonResults).containsExactly(ComparisonResult("subjectId", 42f)) assertThat(successState.totalCandidates).isEqualTo(totalCandidates) assertThat(successState.matcherName).isEqualTo("") // Verify only the size of matchBatches instead of exact content assertThat(successState.matchBatches).hasSize(1) - assertThat(successState.matchBatches[0].count).isEqualTo(faceIdentities.size) + assertThat(successState.matchBatches[0].count).isEqualTo(faceCandidates.size) } } diff --git a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt index df9a0bd045..00893758ac 100644 --- a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt +++ b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt @@ -4,10 +4,12 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.google.common.truth.Truth.* import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.Identity -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.capture.BiometricTemplateCapture +import com.simprints.core.domain.reference.CandidateRecord +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.fingerprint.infra.biosdk.BioSdkWrapper @@ -18,8 +20,8 @@ import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.CandidateRecordBatch +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.logging.LoggingConstants import com.simprints.infra.logging.Simber import com.simprints.infra.matching.MatchParams @@ -87,11 +89,15 @@ internal class FingerprintMatcherUseCaseTest { val results = useCase .invoke( MatchParams( - probeReferenceId = "referenceId", - probeSamples = emptyList(), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = emptyList(), + ), bioSdk = SECUGEN_SIM_MATCHER, flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), project, @@ -113,26 +119,28 @@ internal class FingerprintMatcherUseCaseTest { fun `Skips matching if there are no candidates`() = runTest { coEvery { enrolmentRecordRepository.count(any()) } returns 0 coEvery { - enrolmentRecordRepository.loadIdentities(any(), any(), any(), project, any(), any()) + enrolmentRecordRepository.loadCandidateRecords(any(), any(), any(), project, any(), any()) } returns createTestChannel(emptyList()) coEvery { bioSdkWrapper.match(any(), any(), any()) } returns listOf() val results = useCase .invoke( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf( - CaptureSample( - captureEventId = "fingerprintId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", - identifier = SampleIdentifier.LEFT_3RD_FINGER, + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "fingerprintId", + template = byteArrayOf(1, 2, 3), + identifier = TemplateIdentifier.LEFT_3RD_FINGER, + ), ), ), bioSdk = SECUGEN_SIM_MATCHER, flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), project, @@ -158,19 +166,21 @@ internal class FingerprintMatcherUseCaseTest { val results = useCase .invoke( MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf( - CaptureSample( - captureEventId = "fingerprintId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", - identifier = SampleIdentifier.LEFT_3RD_FINGER, + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "fingerprintId", + template = byteArrayOf(1, 2, 3), + identifier = TemplateIdentifier.LEFT_3RD_FINGER, + ), ), ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, // Wrong SDK type flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), project, @@ -202,7 +212,7 @@ internal class FingerprintMatcherUseCaseTest { coEvery { enrolmentRecordRepository.count(any(), any()) } returns 100 coEvery { createRangesUseCase(any()) } returns listOf(0..99) coEvery { - enrolmentRecordRepository.loadIdentities( + enrolmentRecordRepository.loadCandidateRecords( any(), any(), any(), @@ -212,19 +222,19 @@ internal class FingerprintMatcherUseCaseTest { ) } returns createTestChannel( listOf( - Identity( + CandidateRecord( "personId", listOf( - fingerprintSample(SampleIdentifier.RIGHT_5TH_FINGER), - fingerprintSample(SampleIdentifier.RIGHT_4TH_FINGER), - fingerprintSample(SampleIdentifier.RIGHT_3RD_FINGER), - fingerprintSample(SampleIdentifier.RIGHT_INDEX_FINGER), - fingerprintSample(SampleIdentifier.RIGHT_THUMB), - fingerprintSample(SampleIdentifier.LEFT_THUMB), - fingerprintSample(SampleIdentifier.LEFT_INDEX_FINGER), - fingerprintSample(SampleIdentifier.LEFT_3RD_FINGER), - fingerprintSample(SampleIdentifier.LEFT_4TH_FINGER), - fingerprintSample(SampleIdentifier.LEFT_5TH_FINGER), + fingerprintReference(TemplateIdentifier.RIGHT_5TH_FINGER), + fingerprintReference(TemplateIdentifier.RIGHT_4TH_FINGER), + fingerprintReference(TemplateIdentifier.RIGHT_3RD_FINGER), + fingerprintReference(TemplateIdentifier.RIGHT_INDEX_FINGER), + fingerprintReference(TemplateIdentifier.RIGHT_THUMB), + fingerprintReference(TemplateIdentifier.LEFT_THUMB), + fingerprintReference(TemplateIdentifier.LEFT_INDEX_FINGER), + fingerprintReference(TemplateIdentifier.LEFT_3RD_FINGER), + fingerprintReference(TemplateIdentifier.LEFT_4TH_FINGER), + fingerprintReference(TemplateIdentifier.LEFT_5TH_FINGER), ), ), ), @@ -234,19 +244,21 @@ internal class FingerprintMatcherUseCaseTest { useCase .invoke( matchParams = MatchParams( - probeReferenceId = "referenceId", - probeSamples = listOf( - CaptureSample( - captureEventId = "fingerprintId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", - identifier = SampleIdentifier.LEFT_3RD_FINGER, + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "fingerprintId", + template = byteArrayOf(1, 2, 3), + identifier = TemplateIdentifier.LEFT_3RD_FINGER, + ), ), ), bioSdk = SECUGEN_SIM_MATCHER, flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), project, @@ -254,22 +266,26 @@ internal class FingerprintMatcherUseCaseTest { coVerify { bioSdkWrapper.match(any(), any(), any()) } } - private fun fingerprintSample(finger: SampleIdentifier) = Sample( - identifier = finger, - template = byteArrayOf(1), + private fun fingerprintReference(finger: TemplateIdentifier) = BiometricReference( + templates = listOf( + BiometricTemplate( + identifier = finger, + template = byteArrayOf(1), + ), + ), format = "format", referenceId = "referenceId", modality = Modality.FINGERPRINT, ) } -fun createTestChannel(vararg lists: List): ReceiveChannel { - val channel = Channel(lists.size) +fun createTestChannel(vararg lists: List): ReceiveChannel { + val channel = Channel(lists.size) runBlocking { var time = 0L for (list in lists) { channel.send( - IdentityBatch( + CandidateRecordBatch( identities = list, loadingStartTime = Timestamp(time++), loadingEndTime = Timestamp(time++), diff --git a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/MatchResultSetTest.kt b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/MatchResultSetTest.kt index c41c905a63..81c56f033e 100644 --- a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/MatchResultSetTest.kt +++ b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/MatchResultSetTest.kt @@ -1,7 +1,7 @@ package com.simprints.infra.matching.usecase import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.MatchComparisonResult +import com.simprints.core.domain.comparison.ComparisonResult import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors @@ -12,17 +12,17 @@ class MatchResultSetTest { fun `Stores results sorted descending by confidence up to the limit`() { val set = MatchResultSet(3) - set.add(MatchComparisonResult("4", 0.4f)) - set.add(MatchComparisonResult("1", 0.1f)) - set.add(MatchComparisonResult("3", 0.3f)) - set.add(MatchComparisonResult("3", 0.1f)) - set.add(MatchComparisonResult("2", 0.2f)) + set.add(ComparisonResult("4", 0.4f)) + set.add(ComparisonResult("1", 0.1f)) + set.add(ComparisonResult("3", 0.3f)) + set.add(ComparisonResult("3", 0.1f)) + set.add(ComparisonResult("2", 0.2f)) assertThat(set.toList()).isEqualTo( listOf( - MatchComparisonResult("4", 0.4f), - MatchComparisonResult("3", 0.3f), - MatchComparisonResult("2", 0.2f), + ComparisonResult("4", 0.4f), + ComparisonResult("3", 0.3f), + ComparisonResult("2", 0.2f), ), ) } @@ -30,12 +30,12 @@ class MatchResultSetTest { @Test fun `Merges sets preserving the total limit`() { val setOne = MatchResultSet(2) - setOne.add(MatchComparisonResult("1", 0.1f)) - setOne.add(MatchComparisonResult("3", 0.3f)) + setOne.add(ComparisonResult("1", 0.1f)) + setOne.add(ComparisonResult("3", 0.3f)) val setTwo = MatchResultSet(2) - setTwo.add(MatchComparisonResult("2", 0.2f)) - setTwo.add(MatchComparisonResult("4", 0.4f)) + setTwo.add(ComparisonResult("2", 0.2f)) + setTwo.add(ComparisonResult("4", 0.4f)) val set = MatchResultSet(3) set.addAll(setOne) @@ -43,9 +43,9 @@ class MatchResultSetTest { assertThat(set.toList()).isEqualTo( listOf( - MatchComparisonResult("4", 0.4f), - MatchComparisonResult("3", 0.3f), - MatchComparisonResult("2", 0.2f), + ComparisonResult("4", 0.4f), + ComparisonResult("3", 0.3f), + ComparisonResult("2", 0.2f), ), ) } @@ -55,16 +55,16 @@ class MatchResultSetTest { fun `Stores results sorted descending by confidence and id`() { val set = MatchResultSet(3) - set.add(MatchComparisonResult("4", 0.4f)) - set.add(MatchComparisonResult("1", 0.4f)) - set.add(MatchComparisonResult("3", 0.3f)) - set.add(MatchComparisonResult("2", 0.3f)) + set.add(ComparisonResult("4", 0.4f)) + set.add(ComparisonResult("1", 0.4f)) + set.add(ComparisonResult("3", 0.3f)) + set.add(ComparisonResult("2", 0.3f)) assertThat(set.toList()).isEqualTo( listOf( - MatchComparisonResult("4", 0.4f), - MatchComparisonResult("1", 0.4f), - MatchComparisonResult("3", 0.3f), + ComparisonResult("4", 0.4f), + ComparisonResult("1", 0.4f), + ComparisonResult("3", 0.3f), ), ) } @@ -88,7 +88,7 @@ class MatchResultSetTest { // Each thread adds its own batch of elements repeat(elementsPerThread) { i -> val confidence = (threadIndex * elementsPerThread + i) / 100f - set.add(MatchComparisonResult("T$threadIndex-$i", confidence)) + set.add(ComparisonResult("T$threadIndex-$i", confidence)) } } catch (e: Exception) { e.printStackTrace() @@ -111,11 +111,11 @@ class MatchResultSetTest { // Should be sorted by confidence descending for (i in 0 until results.size - 1) { - assertThat(results[i].confidence).isAtLeast(results[i + 1].confidence) + assertThat(results[i].comparisonScore).isAtLeast(results[i + 1].comparisonScore) } // Verify the highest confidence item is at the top - assertThat(results[0].confidence).isEqualTo(1.99f) + assertThat(results[0].comparisonScore).isEqualTo(1.99f) } @Test @@ -129,7 +129,7 @@ class MatchResultSetTest { MatchResultSet(3).apply { repeat(5) { i -> val confidence = 0.5f + (threadIndex * 5 + i) / 100f - add(MatchComparisonResult("S$threadIndex-$i", confidence)) + add(ComparisonResult("S$threadIndex-$i", confidence)) } } } @@ -163,11 +163,11 @@ class MatchResultSetTest { // Should be sorted by confidence descending for (i in 0 until results.size - 1) { - assertThat(results[i].confidence).isAtLeast(results[i + 1].confidence) + assertThat(results[i].comparisonScore).isAtLeast(results[i + 1].comparisonScore) } // Verify the highest confidence item is at the top - assertThat(results[0].confidence).isEqualTo(0.74f) + assertThat(results[0].comparisonScore).isEqualTo(0.74f) } @Test @@ -175,18 +175,18 @@ class MatchResultSetTest { val set = MatchResultSet(3) // Add higher confidence items first to fill the set - set.add(MatchComparisonResult("A", 0.8f)) - set.add(MatchComparisonResult("B", 0.7f)) - set.add(MatchComparisonResult("C", 0.6f)) + set.add(ComparisonResult("A", 0.8f)) + set.add(ComparisonResult("B", 0.7f)) + set.add(ComparisonResult("C", 0.6f)) // Try to add a new set with lower confidence items val lowerSet = MatchResultSet(3) - lowerSet.add(MatchComparisonResult("D", 0.5f)) - lowerSet.add(MatchComparisonResult("E", 0.4f)) - lowerSet.add(MatchComparisonResult("F", 0.3f)) + lowerSet.add(ComparisonResult("D", 0.5f)) + lowerSet.add(ComparisonResult("E", 0.4f)) + lowerSet.add(ComparisonResult("F", 0.3f)) // Add one higher item to verify it still gets added - lowerSet.add(MatchComparisonResult("G", 0.9f)) + lowerSet.add(ComparisonResult("G", 0.9f)) set.addAll(lowerSet) @@ -194,8 +194,8 @@ class MatchResultSetTest { val results = set.toList() assertThat(results).hasSize(3) - assertThat(results[0].confidence).isEqualTo(0.9f) - assertThat(results[1].confidence).isEqualTo(0.8f) - assertThat(results[2].confidence).isEqualTo(0.7f) + assertThat(results[0].comparisonScore).isEqualTo(0.9f) + assertThat(results[1].comparisonScore).isEqualTo(0.8f) + assertThat(results[2].comparisonScore).isEqualTo(0.7f) } } diff --git a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCaseTest.kt b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCaseTest.kt index cec8168abd..82e4ddbf11 100644 --- a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCaseTest.kt +++ b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/SaveMatchEventUseCaseTest.kt @@ -1,11 +1,12 @@ package com.simprints.infra.matching.usecase import com.google.common.truth.Truth.* +import com.simprints.core.domain.capture.BiometricReferenceCapture +import com.simprints.core.domain.capture.BiometricTemplateCapture import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.CaptureSample -import com.simprints.core.domain.sample.MatchComparisonResult -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier +import com.simprints.core.domain.comparison.ComparisonResult import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.tools.time.Timestamp import com.simprints.infra.config.store.models.FaceConfiguration @@ -13,7 +14,7 @@ import com.simprints.infra.config.store.models.FingerprintConfiguration.BioSdk.S import com.simprints.infra.config.store.models.FingerprintConfiguration.FingerComparisonStrategy import com.simprints.infra.config.sync.ConfigManager import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordQuery import com.simprints.infra.events.event.domain.models.OneToManyMatchEvent import com.simprints.infra.events.event.domain.models.OneToManyMatchEvent.OneToManyMatchPayload.OneToManyMatchPayloadV3 import com.simprints.infra.events.event.domain.models.OneToOneMatchEvent @@ -69,16 +70,18 @@ class SaveMatchEventUseCaseTest { Timestamp(1L), Timestamp(2L), MatchParams( - probeReferenceId = "referenceId", flowType = FlowType.VERIFY, bioSdk = FaceConfiguration.BioSdk.RANK_ONE, - queryForCandidates = SubjectQuery(subjectId = "subjectId"), - probeSamples = listOf( - CaptureSample( - captureEventId = "faceId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FACE, - format = "format", + queryForCandidates = EnrolmentRecordQuery(subjectId = "subjectId"), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FACE, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "fingerprintId", + template = byteArrayOf(1, 2, 3), + ), ), ), biometricDataSource = BiometricDataSource.Simprints, @@ -86,8 +89,8 @@ class SaveMatchEventUseCaseTest { 2, "faceMatcherName", listOf( - MatchComparisonResult("guid1", 0.5f), - MatchComparisonResult("guid2", 0.1f), + ComparisonResult("guid1", 0.5f), + ComparisonResult("guid2", 0.1f), ), batches = emptyList(), ) @@ -115,16 +118,18 @@ class SaveMatchEventUseCaseTest { Timestamp(1L), Timestamp(2L), MatchParams( - probeReferenceId = "referenceId", flowType = FlowType.VERIFY, - queryForCandidates = SubjectQuery(subjectId = "subjectId"), - probeSamples = listOf( - CaptureSample( - captureEventId = "fingerprintId", - template = byteArrayOf(1, 2, 3), - modality = Modality.FINGERPRINT, - format = "format", - identifier = SampleIdentifier.RIGHT_5TH_FINGER, + queryForCandidates = EnrolmentRecordQuery(subjectId = "subjectId"), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = listOf( + BiometricTemplateCapture( + captureEventId = "fingerprintId", + template = byteArrayOf(1, 2, 3), + identifier = TemplateIdentifier.LEFT_3RD_FINGER, + ), ), ), bioSdk = SECUGEN_SIM_MATCHER, @@ -133,8 +138,8 @@ class SaveMatchEventUseCaseTest { 2, "faceMatcherName", listOf( - MatchComparisonResult("guid1", 0.5f), - MatchComparisonResult("guid2", 0.1f), + ComparisonResult("guid1", 0.5f), + ComparisonResult("guid2", 0.1f), ), batches = emptyList(), ) @@ -166,18 +171,22 @@ class SaveMatchEventUseCaseTest { startTime = Timestamp(1L), endTime = Timestamp(2L), matchParams = MatchParams( - probeReferenceId = "referenceId", - probeSamples = emptyList(), + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = emptyList(), + ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, flowType = FlowType.IDENTIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), candidatesCount = 2, matcherName = "faceMatcherName", results = listOf( - MatchComparisonResult("guid1", 0.5f), - MatchComparisonResult("guid2", 0.1f), + ComparisonResult("guid1", 0.5f), + ComparisonResult("guid2", 0.1f), ), batches = batches, ) @@ -227,15 +236,20 @@ class SaveMatchEventUseCaseTest { Timestamp(1L), Timestamp(2L), MatchParams( - probeReferenceId = "referenceId", + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = emptyList(), + ), flowType = FlowType.IDENTIFY, bioSdk = FaceConfiguration.BioSdk.RANK_ONE, - queryForCandidates = SubjectQuery(attendantId = "userId".asTokenizableEncrypted()), + queryForCandidates = EnrolmentRecordQuery(attendantId = "userId".asTokenizableEncrypted()), biometricDataSource = BiometricDataSource.Simprints, ), 0, "faceMatcherName", - listOf(MatchComparisonResult("guid1", 0.5f)), + listOf(ComparisonResult("guid1", 0.5f)), batches = batches, ) @@ -265,10 +279,15 @@ class SaveMatchEventUseCaseTest { Timestamp(1L), Timestamp(2L), MatchParams( - probeReferenceId = "referenceId", + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = emptyList(), + ), flowType = FlowType.IDENTIFY, bioSdk = FaceConfiguration.BioSdk.RANK_ONE, - queryForCandidates = SubjectQuery(moduleId = "moduleId".asTokenizableEncrypted()), + queryForCandidates = EnrolmentRecordQuery(moduleId = "moduleId".asTokenizableEncrypted()), biometricDataSource = BiometricDataSource.Simprints, ), 0, @@ -303,11 +322,15 @@ class SaveMatchEventUseCaseTest { Timestamp(1L), Timestamp(2L), MatchParams( - probeReferenceId = "referenceId", + probeReference = BiometricReferenceCapture( + referenceId = "referenceId", + modality = Modality.FINGERPRINT, + format = "format", + templates = emptyList(), + ), bioSdk = FaceConfiguration.BioSdk.RANK_ONE, - probeSamples = emptyList(), flowType = FlowType.IDENTIFY, - queryForCandidates = SubjectQuery(), + queryForCandidates = EnrolmentRecordQuery(), biometricDataSource = BiometricDataSource.Simprints, ), 0, diff --git a/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt b/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt index d56314875d..fa1028fc5f 100644 --- a/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt +++ b/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt @@ -3,7 +3,7 @@ package com.simprints.infra.sync.config.testtools import com.simprints.core.domain.common.AgeGroup import com.simprints.core.domain.common.Modality import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.infra.config.store.models.ConsentConfiguration import com.simprints.infra.config.store.models.DecisionPolicy @@ -68,7 +68,7 @@ internal val fingerprintConfiguration = FingerprintConfiguration( allowedSDKs = listOf(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), displayHandIcons = true, secugenSimMatcher = FingerprintConfiguration.FingerprintSdkConfiguration( - listOf(SampleIdentifier.LEFT_3RD_FINGER), + listOf(TemplateIdentifier.LEFT_3RD_FINGER), decisionPolicy, FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(10), diff --git a/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt b/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt index 817c317466..6573852531 100644 --- a/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt +++ b/testing/data-generator/src/main/java/com/simprints/feature/datagenerator/enrollmentrecords/InsertEnrollmentRecordsUseCase.kt @@ -3,14 +3,15 @@ package com.simprints.feature.datagenerator.enrollmentrecords import android.os.Bundle import com.simprints.core.DispatcherIO import com.simprints.core.domain.common.Modality -import com.simprints.core.domain.sample.Sample -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.reference.BiometricReference +import com.simprints.core.domain.reference.BiometricTemplate +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.core.domain.tokenization.asTokenizableEncrypted import com.simprints.core.tools.time.TimeHelper import com.simprints.infra.config.store.ConfigRepository import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.Subject -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecord +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import com.simprints.infra.enrolment.records.repository.local.models.toDate import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow @@ -40,24 +41,24 @@ internal class InsertEnrollmentRecordsUseCase @Inject constructor( val creationDate = timeHelper.now().ms.toDate() val updateDate = System.currentTimeMillis().toDate() // generate n records and insert them in the repo the first subjectId is used for only one record the rest are generated randomly - var subjectCreationActions = mutableListOf() + var subjectCreationActions = mutableListOf() for (i in 0 until numRecords) { val subjectId = if (i == 0 && firstSubjectId.isNotBlank()) firstSubjectId else UUID.randomUUID().toString() - val subject = Subject( + val enrolmentRecord = EnrolmentRecord( subjectId = subjectId, projectId = projectId, attendantId = tokenizedAttendantId, moduleId = tokenizedModuleId, createdAt = creationDate, updatedAt = updateDate, - samples = generateFingerprintTemplates( + references = generateFingerprintReferences( templatesPerFormat = templatesPerFormat, fingerOrder = fingerOrder, - ) + generateFaceSamples( + ) + generateFaceReferences( templatesPerFormat = templatesPerFormat, ), ) - subjectCreationActions.add(SubjectAction.Creation(subject)) + subjectCreationActions.add(EnrolmentRecordAction.Creation(enrolmentRecord)) if (subjectCreationActions.size >= BATCH_SIZE) { emit("Inserted ${i + 1} biometric records") enrolmentRecordRepository.performActions( @@ -76,34 +77,37 @@ internal class InsertEnrollmentRecordsUseCase @Inject constructor( emit("Inserted $numRecords biometric records") }.flowOn(dispatcher) - private fun generateFaceSamples(templatesPerFormat: Bundle): List { - val faceSamples = mutableListOf() + private fun generateFaceReferences(templatesPerFormat: Bundle): List { + val faceReferences = mutableListOf() for (key in templatesPerFormat.keySet()) { if (FINGERPRINT_FORMATES.contains(key)) { // Skip non-face formats continue } val numSamples = templatesPerFormat.getInt(key, 0) - repeat(numSamples) { - faceSamples.add( - Sample( - template = getTemplateForFormat(key), - format = key, - referenceId = UUID.randomUUID().toString(), - id = UUID.randomUUID().toString(), - modality = Modality.FACE, - ), - ) - } + + faceReferences.add( + BiometricReference( + templates = List(numSamples) { it }.map { + BiometricTemplate( + id = UUID.randomUUID().toString(), + template = getTemplateForFormat(key), + ) + }, + format = key, + referenceId = UUID.randomUUID().toString(), + modality = Modality.FACE, + ), + ) } - return faceSamples + return faceReferences } - private fun generateFingerprintTemplates( + private fun generateFingerprintReferences( templatesPerFormat: Bundle, fingerOrder: Bundle?, - ): List { - val fingerprintSamples = mutableListOf() + ): List { + val fingerprintReferences = mutableListOf() for (key in templatesPerFormat.keySet()) { if (FACE_FORMATES.contains(key)) { @@ -113,40 +117,40 @@ internal class InsertEnrollmentRecordsUseCase @Inject constructor( // finger order is a comma separated string of finger identifiers val fingerIdentifiers = fingerOrder?.getString(key, "")?.split(",") val numSamples = templatesPerFormat.getInt(key, 0) - for (i in 0 until numSamples) { - fingerprintSamples.add( - Sample( - template = getTemplateForFormat(key), - format = key, - referenceId = UUID.randomUUID().toString(), - id = UUID.randomUUID().toString(), - identifier = if (fingerIdentifiers.isNullOrEmpty()) { - SampleIdentifier.LEFT_THUMB - } else { - fingerIdentifiers[i % fingerIdentifiers.size].toFingerIdentifier() - }, - modality = Modality.FINGERPRINT, - ), - ) - } + + fingerprintReferences.add( + BiometricReference( + referenceId = UUID.randomUUID().toString(), + format = key, + modality = Modality.FINGERPRINT, + templates = List(numSamples) { it }.map { i -> + BiometricTemplate( + template = getTemplateForFormat(key), + identifier = if (fingerIdentifiers.isNullOrEmpty()) { + TemplateIdentifier.LEFT_THUMB + } else { + fingerIdentifiers[i % fingerIdentifiers.size].toFingerIdentifier() + }, + ) + }, + ), + ) } - return fingerprintSamples + return fingerprintReferences } private fun String.toFingerIdentifier() = when (this.uppercase()) { - "LEFT_THUMB" -> SampleIdentifier.LEFT_THUMB - "LEFT_INDEX_FINGER" -> SampleIdentifier.LEFT_INDEX_FINGER - "LEFT_3RD_FINGER" -> SampleIdentifier.LEFT_3RD_FINGER - "LEFT_4TH_FINGER" -> SampleIdentifier.LEFT_4TH_FINGER - "LEFT_5TH_FINGER" -> SampleIdentifier.LEFT_5TH_FINGER - "RIGHT_THUMB" -> SampleIdentifier.RIGHT_THUMB - "RIGHT_INDEX_FINGER" -> SampleIdentifier.RIGHT_INDEX_FINGER - "RIGHT_3RD_FINGER" -> SampleIdentifier.RIGHT_3RD_FINGER - "RIGHT_4TH_FINGER" -> SampleIdentifier.RIGHT_4TH_FINGER - "RIGHT_5TH_FINGER" -> SampleIdentifier.RIGHT_5TH_FINGER - else -> { - SampleIdentifier.LEFT_THUMB - } + "LEFT_THUMB" -> TemplateIdentifier.LEFT_THUMB + "LEFT_INDEX_FINGER" -> TemplateIdentifier.LEFT_INDEX_FINGER + "LEFT_3RD_FINGER" -> TemplateIdentifier.LEFT_3RD_FINGER + "LEFT_4TH_FINGER" -> TemplateIdentifier.LEFT_4TH_FINGER + "LEFT_5TH_FINGER" -> TemplateIdentifier.LEFT_5TH_FINGER + "RIGHT_THUMB" -> TemplateIdentifier.RIGHT_THUMB + "RIGHT_INDEX_FINGER" -> TemplateIdentifier.RIGHT_INDEX_FINGER + "RIGHT_3RD_FINGER" -> TemplateIdentifier.RIGHT_3RD_FINGER + "RIGHT_4TH_FINGER" -> TemplateIdentifier.RIGHT_4TH_FINGER + "RIGHT_5TH_FINGER" -> TemplateIdentifier.RIGHT_5TH_FINGER + else -> TemplateIdentifier.LEFT_THUMB } private fun getTemplateForFormat(format: String): ByteArray = when (format) { diff --git a/testing/data-generator/src/test/java/com/simprints/feature/datagenerator/InsertEnrollmentRecordsUseCaseTest.kt b/testing/data-generator/src/test/java/com/simprints/feature/datagenerator/InsertEnrollmentRecordsUseCaseTest.kt index fb24923edb..120b174487 100644 --- a/testing/data-generator/src/test/java/com/simprints/feature/datagenerator/InsertEnrollmentRecordsUseCaseTest.kt +++ b/testing/data-generator/src/test/java/com/simprints/feature/datagenerator/InsertEnrollmentRecordsUseCaseTest.kt @@ -4,13 +4,13 @@ import android.os.Bundle import androidx.core.os.bundleOf import androidx.test.ext.junit.runners.* import com.google.common.truth.Truth.* -import com.simprints.core.domain.sample.SampleIdentifier +import com.simprints.core.domain.common.TemplateIdentifier import com.simprints.feature.datagenerator.enrollmentrecords.InsertEnrollmentRecordsUseCase import com.simprints.feature.datagenerator.enrollmentrecords.InsertEnrollmentRecordsUseCase.Companion.BATCH_SIZE import com.simprints.infra.config.store.ConfigRepository import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository -import com.simprints.infra.enrolment.records.repository.domain.models.SubjectAction +import com.simprints.infra.enrolment.records.repository.domain.models.EnrolmentRecordAction import io.mockk.* import io.mockk.impl.annotations.MockK import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -70,8 +70,8 @@ internal class InsertEnrollmentRecordsUseCaseTest { fun `invoke with less than batch size records should insert one batch`() = runTest { // Given val numRecords = 10 - val subjectActionsSlot = slot>() - coEvery { enrolmentRecordRepository.performActions(capture(subjectActionsSlot), any()) } returns mockk() + val enrolmentRecordActionsSlot = slot>() + coEvery { enrolmentRecordRepository.performActions(capture(enrolmentRecordActionsSlot), any()) } returns mockk() // When val result = useCase( @@ -87,14 +87,14 @@ internal class InsertEnrollmentRecordsUseCaseTest { // Then assertThat(result).isEqualTo("Inserted $numRecords biometric records") coVerify(exactly = 1) { enrolmentRecordRepository.performActions(any(), any()) } - assertThat(subjectActionsSlot.captured).hasSize(numRecords) + assertThat(enrolmentRecordActionsSlot.captured).hasSize(numRecords) } @Test fun `invoke with exactly batch size records should insert one full batch`() = runTest { // Given - val subjectActionsSlot = slot>() - coEvery { enrolmentRecordRepository.performActions(capture(subjectActionsSlot), any()) } returns mockk() + val enrolmentRecordActionsSlot = slot>() + coEvery { enrolmentRecordRepository.performActions(capture(enrolmentRecordActionsSlot), any()) } returns mockk() // When val result = useCase( @@ -110,14 +110,14 @@ internal class InsertEnrollmentRecordsUseCaseTest { // Then assertThat(result).isEqualTo("Inserted $BATCH_SIZE biometric records") coVerify(exactly = 1) { enrolmentRecordRepository.performActions(any(), any()) } - assertThat(subjectActionsSlot.captured).hasSize(BATCH_SIZE) + assertThat(enrolmentRecordActionsSlot.captured).hasSize(BATCH_SIZE) } @Test fun `invoke with more than batch size records should insert multiple batches`() = runTest { // Given val numRecords = BATCH_SIZE + 10 - val capturedActions = mutableListOf>() + val capturedActions = mutableListOf>() coEvery { enrolmentRecordRepository.performActions(capture(capturedActions), any()) } returns mockk() // When @@ -143,8 +143,8 @@ internal class InsertEnrollmentRecordsUseCaseTest { // Given val numRecords = 3 val firstId = "test-subject-id-123" - val subjectActionsSlot = slot>() - coEvery { enrolmentRecordRepository.performActions(capture(subjectActionsSlot), any()) } returns mockk() + val enrolmentRecordActionsSlot = slot>() + coEvery { enrolmentRecordRepository.performActions(capture(enrolmentRecordActionsSlot), any()) } returns mockk() // When useCase( @@ -158,7 +158,7 @@ internal class InsertEnrollmentRecordsUseCaseTest { ).last() // Then - val subjects = subjectActionsSlot.captured.map { it.subject } + val subjects = enrolmentRecordActionsSlot.captured.map { it.enrolmentRecord } assertThat(subjects).hasSize(numRecords) assertThat(subjects[0].subjectId).isEqualTo(firstId) assertThat(subjects[1].subjectId).isNotEqualTo(firstId) @@ -170,8 +170,8 @@ internal class InsertEnrollmentRecordsUseCaseTest { fun `invoke with blank firstSubjectId should generate random IDs for all records`() = runTest { // Given val numRecords = 3 - val subjectActionsSlot = slot>() - coEvery { enrolmentRecordRepository.performActions(capture(subjectActionsSlot), any()) } returns mockk() + val enrolmentRecordActionsSlot = slot>() + coEvery { enrolmentRecordRepository.performActions(capture(enrolmentRecordActionsSlot), any()) } returns mockk() // When useCase( @@ -185,7 +185,7 @@ internal class InsertEnrollmentRecordsUseCaseTest { ).last() // Then - val subjects = subjectActionsSlot.captured.map { it.subject } + val subjects = enrolmentRecordActionsSlot.captured.map { it.enrolmentRecord } assertThat(subjects).hasSize(numRecords) assertThat(subjects[0].subjectId).isNotEmpty() assertThat(subjects[0].subjectId).isNotEqualTo(" ") @@ -200,8 +200,8 @@ internal class InsertEnrollmentRecordsUseCaseTest { putInt("SIM_FACE_BASE_1", 2) // 2 face samples putInt("NEC_1_5", 6) // 6 fingerprint samples } - val subjectActionsSlot = slot>() - coEvery { enrolmentRecordRepository.performActions(capture(subjectActionsSlot), any()) } returns mockk() + val enrolmentRecordActionsSlot = slot>() + coEvery { enrolmentRecordRepository.performActions(capture(enrolmentRecordActionsSlot), any()) } returns mockk() // When useCase( @@ -218,9 +218,18 @@ internal class InsertEnrollmentRecordsUseCaseTest { ).last() // Then - val subject = subjectActionsSlot.captured.first().subject - assertThat(subject.samples.count { it.format == "SIM_FACE_BASE_1" }).isEqualTo(2) - assertThat(subject.samples.count { it.format == "NEC_1_5" }).isEqualTo(6) + val subject = enrolmentRecordActionsSlot.captured.first().enrolmentRecord + assertThat(subject.references.size).isEqualTo(2) + assertThat( + subject.references + .find { it.format == "SIM_FACE_BASE_1" } + ?.templates, + ).hasSize(2) + assertThat( + subject.references + .find { it.format == "NEC_1_5" } + ?.templates, + ).hasSize(6) } @Test @@ -229,8 +238,8 @@ internal class InsertEnrollmentRecordsUseCaseTest { val templatesPerFormat = Bundle().apply { putInt("ISO_19794_2", 2) } - val subjectActionsSlot = slot>() - coEvery { enrolmentRecordRepository.performActions(capture(subjectActionsSlot), any()) } returns mockk() + val enrolmentRecordActionsSlot = slot>() + coEvery { enrolmentRecordRepository.performActions(capture(enrolmentRecordActionsSlot), any()) } returns mockk() // When useCase( @@ -244,10 +253,21 @@ internal class InsertEnrollmentRecordsUseCaseTest { ).last() // Then - val subject = subjectActionsSlot.captured.first().subject - assertThat(subject.samples).hasSize(2) - assertThat(subject.samples[0].identifier).isEqualTo(SampleIdentifier.LEFT_THUMB) - assertThat(subject.samples[1].identifier).isEqualTo(SampleIdentifier.LEFT_THUMB) + val subject = enrolmentRecordActionsSlot.captured.first().enrolmentRecord + assertThat(subject.references).hasSize(1) + assertThat(subject.references[0].templates).hasSize(2) + assertThat( + subject.references[0] + .templates + .first() + .identifier, + ).isEqualTo(TemplateIdentifier.LEFT_THUMB) + assertThat( + subject.references[0] + .templates + .last() + .identifier, + ).isEqualTo(TemplateIdentifier.LEFT_THUMB) } @Test @@ -261,8 +281,8 @@ internal class InsertEnrollmentRecordsUseCaseTest { // 2 fingers, so it should cycle putString(format, "RIGHT_THUMB,RIGHT_INDEX_FINGER") } - val subjectActionsSlot = slot>() - coEvery { enrolmentRecordRepository.performActions(capture(subjectActionsSlot), any()) } returns mockk() + val enrolmentRecordActionsSlot = slot>() + coEvery { enrolmentRecordRepository.performActions(capture(enrolmentRecordActionsSlot), any()) } returns mockk() // When useCase( @@ -276,15 +296,18 @@ internal class InsertEnrollmentRecordsUseCaseTest { ).last() // Then - val subject = subjectActionsSlot.captured.first().subject - val fingers = subject.samples.map { it.identifier } + val subject = enrolmentRecordActionsSlot.captured.first().enrolmentRecord + val fingers = subject.references + .first() + .templates + .map { it.identifier } assertThat(fingers).hasSize(4) assertThat(fingers) .containsExactly( - SampleIdentifier.RIGHT_THUMB, - SampleIdentifier.RIGHT_INDEX_FINGER, - SampleIdentifier.RIGHT_THUMB, // cycles back - SampleIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.RIGHT_THUMB, + TemplateIdentifier.RIGHT_INDEX_FINGER, + TemplateIdentifier.RIGHT_THUMB, // cycles back + TemplateIdentifier.RIGHT_INDEX_FINGER, ).inOrder() } }