From 1a575e4bd69d93a8ad22e59d7bd48314de5830cf Mon Sep 17 00:00:00 2001 From: Sergejs Luhmirins Date: Wed, 28 Aug 2024 12:50:09 +0300 Subject: [PATCH 1/2] MS-276 Take subject query into account when requesting data from commcare provider --- .../commcare/CommCareIdentityDataSource.kt | 107 ++++---- .../CommCareIdentityDataSourceTest.kt | 256 +++++++++++++----- 2 files changed, 241 insertions(+), 122 deletions(-) diff --git a/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt b/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt index befd18c93b..b2202c2a29 100644 --- a/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt +++ b/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt @@ -1,6 +1,7 @@ package com.simprints.infra.enrolment.records.store.commcare import android.content.Context +import android.database.Cursor import android.net.Uri import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.module.SimpleModule @@ -18,6 +19,7 @@ import com.simprints.infra.enrolment.records.store.domain.models.FingerprintIden import com.simprints.infra.enrolment.records.store.domain.models.SubjectQuery import com.simprints.infra.events.event.cosync.CoSyncEnrolmentRecordEvents import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordCreationEvent +import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordEvent import com.simprints.infra.events.event.domain.models.subject.FaceReference import com.simprints.infra.events.event.domain.models.subject.FingerprintReference import com.simprints.infra.logging.Simber @@ -48,7 +50,7 @@ internal class CommCareIdentityDataSource @Inject constructor( query: SubjectQuery, range: IntRange, dataSource: BiometricDataSource, - ): List = loadEnrolmentRecordCreationEvents(range, dataSource.callerPackageName()) + ): List = loadEnrolmentRecordCreationEvents(range, dataSource.callerPackageName(), query) .filter { erce -> erce.payload.biometricReferences.any { it is FingerprintReference } } .map { FingerprintIdentity( @@ -70,20 +72,22 @@ internal class CommCareIdentityDataSource @Inject constructor( private fun loadEnrolmentRecordCreationEvents( range: IntRange, callerPackageName: String, + query: SubjectQuery, ): List { val enrolmentRecordCreationEvents: MutableList = mutableListOf() try { context.contentResolver.query( - getCaseMetadataUri(callerPackageName), null, null, null, null)?.use { caseMetadataCursor -> - if (caseMetadataCursor.moveToPosition(range.first)) { - do { - caseMetadataCursor.getString(caseMetadataCursor.getColumnIndexOrThrow(COLUMN_CASE_ID))?.let { caseId -> - enrolmentRecordCreationEvents.addAll(loadEnrolmentRecordCreationEvents(caseId, callerPackageName)) - } - } while (caseMetadataCursor.moveToNext() && caseMetadataCursor.position < range.last) - } + getCaseMetadataUri(callerPackageName), null, null, null, null + )?.use { caseMetadataCursor -> + if (caseMetadataCursor.moveToPosition(range.first)) { + do { + caseMetadataCursor.getString(caseMetadataCursor.getColumnIndexOrThrow(COLUMN_CASE_ID))?.let { caseId -> + enrolmentRecordCreationEvents.addAll(loadEnrolmentRecordCreationEvents(caseId, callerPackageName, query)) + } + } while (caseMetadataCursor.moveToNext() && caseMetadataCursor.position < range.last) } + } } catch (e: Exception) { Simber.e("Error while querying CommCare", e) } @@ -95,7 +99,7 @@ internal class CommCareIdentityDataSource @Inject constructor( query: SubjectQuery, range: IntRange, dataSource: BiometricDataSource, - ): List = loadEnrolmentRecordCreationEvents(range, dataSource.callerPackageName()) + ): List = loadEnrolmentRecordCreationEvents(range, dataSource.callerPackageName(), query) .filter { erce -> erce.payload.biometricReferences.any { it is FaceReference } } .map { FaceIdentity( @@ -115,55 +119,58 @@ internal class CommCareIdentityDataSource @Inject constructor( private fun loadEnrolmentRecordCreationEvents( caseId: String, callerPackageName: String, + query: SubjectQuery, ): List { - val caseEnrolmentRecordCreationEvents: MutableList = - mutableListOf() - //Access Case Data Listing for the caseId val caseDataUri = getCaseDataUri(callerPackageName).buildUpon().appendPath(caseId).build() - context.contentResolver.query(caseDataUri, null, null, null, null)?.use { caseDataCursor -> - var subjectActions = "" - while (caseDataCursor.moveToNext()) { - val key = - caseDataCursor.getString(caseDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID)) - if (key == SIMPRINTS_COSYNC_SUBJECT_ACTIONS) { - subjectActions = - caseDataCursor.getString(caseDataCursor.getColumnIndexOrThrow(COLUMN_VALUE)) - break - } - } - val coSyncEnrolmentRecordEvents = subjectActions.takeIf(String::isNotEmpty)?.let { - try { - val coSyncSerializationModule = SimpleModule().apply { - addSerializer( - TokenizableString::class.java, - TokenizationClassNameSerializer() - ) - addDeserializer( - TokenizableString::class.java, - TokenizationClassNameDeserializer() - ) - } - jsonHelper.fromJson( - json = it, - module = coSyncSerializationModule, - type = object : TypeReference() {} - ) - } catch (e: Exception) { - Simber.e("Error while parsing subjectActions", e) - null + return context.contentResolver.query(caseDataUri, null, null, null, null)?.use { caseDataCursor -> + var subjectActions = getSubjectActionsValue(caseDataCursor) + Simber.d(subjectActions) + val coSyncEnrolmentRecordEvents = parseRecordEvents(subjectActions) + + coSyncEnrolmentRecordEvents?.events + ?.filterIsInstance() + ?.filterNot { event -> + (query.subjectId != null && query.subjectId != event.payload.subjectId) + || (query.attendantId != null && query.attendantId != event.payload.attendantId.value) + || (query.moduleId != null && query.moduleId != event.payload.moduleId.value) } + }.orEmpty() + } + + private fun getSubjectActionsValue(caseDataCursor: Cursor): String { + while (caseDataCursor.moveToNext()) { + val key = caseDataCursor.getString(caseDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID)) + if (key == SIMPRINTS_COSYNC_SUBJECT_ACTIONS) { + return caseDataCursor.getString(caseDataCursor.getColumnIndexOrThrow(COLUMN_VALUE)) } - coSyncEnrolmentRecordEvents?.events?.filterIsInstance() - ?.let { events -> - caseEnrolmentRecordCreationEvents.addAll(events) - } + } + return "" + } - Simber.d(subjectActions) + private fun parseRecordEvents(subjectActions: String) = subjectActions.takeIf(String::isNotEmpty)?.let { + try { + jsonHelper.fromJson( + json = it, + module = coSyncSerializationModule, + type = object : TypeReference() {} + ) + } catch (e: Exception) { + Simber.e("Error while parsing subjectActions", e) + null } + } - return caseEnrolmentRecordCreationEvents + private val coSyncSerializationModule = SimpleModule().apply { + addSerializer( + TokenizableString::class.java, + TokenizationClassNameSerializer() + ) + addDeserializer( + TokenizableString::class.java, + TokenizationClassNameDeserializer() + ) } override suspend fun count( diff --git a/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt b/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt index b243159b20..4e10d723f2 100644 --- a/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt +++ b/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt @@ -30,7 +30,7 @@ class CommCareIdentityDataSourceTest { companion object { 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":"Rk1SACAyMAAAAADkAAABLAGQAMUAxQEAABBjIYCzAAgQAEAhABoeAIB3ACQNAEAoACsYAEDvADYDAECrAFgDAEBQAHIaAEDUAH2AAEEEAIX4AEDhAJX4AEDpALZ7AIB1AL0RAEEJAMPuAECSAM0NAED2ANPxAEDmAOXsAEDdAPrnAEBhAQYaAIDcARDaAICwARn8AEDJASlnAECMATcVAEB7ATgbAECxAT2qAECnAT6hAEB3AT6eAECIAT6eAEEWAUDGAIDHAUizAED9AU2zAEDsAVU9AED1AWYzAIC2AWciAAAA","finger":"LEFT_THUMB"},{"quality":88,"template":"Rk1SACAyMAAAAAEUAAABLAGQAMUAxQEAABBYKUBqACiHAIBKAD0RAECyAEjuAEAfAF6sAEAnAG4sAIB4AHT2AIByAIuiAEAGAIs1AEC9AJDUAEB3AJS9AICaAJXRAEDzAJ/bAECHAKi9AIBzALJDAEAXALfDAEB6ALszAIDGAL3JAED2AL/QAEBHAMrRAEENAMrWAECiAMy9AIAkANfNAEDwANnKAEB3AOr4AICjAPKhAEANAPXGAIEEAPjJAEBCAPzaAICNAQUDAEEZAQdpAEDOARsfAIDpAR0TAEDZASUUAEDnASeNAIDsAS0NAED+ATONAEDxAT8KAIB9AUB5AEDAAVUNAEBdAVZzAECCAWt9AAAA","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":"Rk1SACAyMAAAAADkAAABLAGQAMUAxQEAABBjIYCzAAgQAEAhABoeAIB3ACQNAEAoACsYAEDvADYDAECrAFgDAEBQAHIaAEDUAH2AAEEEAIX4AEDhAJX4AEDpALZ7AIB1AL0RAEEJAMPuAECSAM0NAED2ANPxAEDmAOXsAEDdAPrnAEBhAQYaAIDcARDaAICwARn8AEDJASlnAECMATcVAEB7ATgbAECxAT2qAECnAT6hAEB3AT6eAECIAT6eAEEWAUDGAIDHAUizAED9AU2zAEDsAVU9AED1AWYzAIC2AWciAAAA","finger":"LEFT_THUMB"},{"quality":66,"template":"Rk1SACAyMAAAAAEUAAABLAGQAMUAxQEAABBYKUBqACiHAIBKAD0RAECyAEjuAEAfAF6sAEAnAG4sAIB4AHT2AIByAIuiAEAGAIs1AEC9AJDUAEB3AJS9AICaAJXRAEDzAJ/bAECHAKi9AIBzALJDAEAXALfDAEB6ALszAIDGAL3JAED2AL/QAEBHAMrRAEENAMrWAECiAMy9AIAkANfNAEDwANnKAEB3AOr4AICjAPKhAEANAPXGAIEEAPjJAEBCAPzaAICNAQUDAEEZAQdpAEDOARsfAIDpAR0TAEDZASUUAEDnASeNAIDsAS0NAED+ATONAEDxAT8KAIB9AUB5AEDAAVUNAEBdAVZzAECCAWt9AAAA","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":"ePZ3yiWTt9etQAWu+A3H0WGtHI2uod=="},"attendantId":{"value":"r9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzS="},"biometricReferences":[{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"quality":77,"template":"Rk1SACAyMAAAAADkAAABLAGQAMUAxQEAABBjIYCzAAgQAEAhABoeAIB3ACQNAEAoACsYAEDvADYDAECrAFgDAEBQAHIaAEDUAH2AAEEEAIX4AEDhAJX4AEDpALZ7AIB1AL0RAEEJAMPuAECSAM0NAED2ANPxAEDmAOXsAEDdAPrnAEBhAQYaAIDcARDaAICwARn8AEDJASlnAECMATcVAEB7ATgbAECxAT2qAECnAT6hAEB3AT6eAECIAT6eAEEWAUDGAIDHAUizAED9AU2zAEDsAVU9AED1AWYzAIC2AWciAAAA","finger":"LEFT_THUMB"},{"quality":66,"template":"Rk1SACAyMAAAAAEUAAABLAGQAMUAxQEAABBYKUBqACiHAIBKAD0RAECyAEjuAEAfAF6sAEAnAG4sAIB4AHT2AIByAIuiAEAGAIs1AEC9AJDUAEB3AJS9AICaAJXRAEDzAJ/bAECHAKi9AIBzALJDAEAXALfDAEB6ALszAIDGAL3JAED2AL/QAEBHAMrRAEENAMrWAECiAMy9AIAkANfNAEDwANnKAEB3AOr4AICjAPKhAEANAPXGAIEEAPjJAEBCAPzaAICNAQUDAEEZAQdpAEDOARsfAIDpAR0TAEDZASUUAEDnASeNAIDsAS0NAED+ATONAEDxAT8KAIB9AUB5AEDAAVUNAEBdAVZzAECCAWt9AAAA","finger":"LEFT_INDEX_FINGER"}],"format":"ISO_19794_2","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":"Rk1SACAyMAAAAADkAAABLAGQAMUAxQEAABBjIYCzAAgQAEAhABoeAIB3ACQNAEAoACsYAEDvADYDAECrAFgDAEBQAHIaAEDUAH2AAEEEAIX4AEDhAJX4AEDpALZ7AIB1AL0RAEEJAMPuAECSAM0NAED2ANPxAEDmAOXsAEDdAPrnAEBhAQYaAIDcARDaAICwARn8AEDJASlnAECMATcVAEB7ATgbAECxAT2qAECnAT6hAEB3AT6eAECIAT6eAEEWAUDGAIDHAUizAED9AU2zAEDsAVU9AED1AWYzAIC2AWciAAAA"}],"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":"Rk1SACAyMAAAAADkAAABLAGQAMUAxQEAABBjIYCzAAgQAEAhABoeAIB3ACQNAEAoACsYAEDvADYDAECrAFgDAEBQAHIaAEDUAH2AAEEEAIX4AEDhAJX4AEDpALZ7AIB1AL0RAEEJAMPuAECSAM0NAED2ANPxAEDmAOXsAEDdAPrnAEBhAQYaAIDcARDaAICwARn8AEDJASlnAECMATcVAEB7ATgbAECxAT2qAECnAT6hAEB3AT6eAECIAT6eAEEWAUDGAIDHAUizAED9AU2zAEDsAVU9AED1AWYzAIC2AWciAAAA"}],"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":"Rk1SACAyMAAAAADkAAABLAGQAMUAxQEAABBjIYCzAAgQAEAhABoeAIB3ACQNAEAoACsYAEDvADYDAECrAFgDAEBQAHIaAEDUAH2AAEEEAIX4AEDhAJX4AEDpALZ7AIB1AL0RAEEJAMPuAECSAM0NAED2ANPxAEDmAOXsAEDdAPrnAEBhAQYaAIDcARDaAICwARn8AEDJASlnAECMATcVAEB7ATgbAECxAT2qAECnAT6hAEB3AT6eAECIAT6eAEEWAUDGAIDHAUizAED9AU2zAEDsAVU9AED1AWYzAIC2AWciAAAA","finger":"LEFT_THUMB"},{"quality":88,"template":"Rk1SACAyMAAAAAEUAAABLAGQAMUAxQEAABBYKUBqACiHAIBKAD0RAECyAEjuAEAfAF6sAEAnAG4sAIB4AHT2AIByAIuiAEAGAIs1AEC9AJDUAEB3AJS9AICaAJXRAEDzAJ/bAECHAKi9AIBzALJDAEAXALfDAEB6ALszAIDGAL3JAED2AL/QAEBHAMrRAEENAMrWAECiAMy9AIAkANfNAEDwANnKAEB3AOr4AICjAPKhAEANAPXGAIEEAPjJAEBCAPzaAICNAQUDAEEZAQdpAEDOARsfAIDpAR0TAEDZASUUAEDnASeNAIDsAS0NAED+ATONAEDxAT8KAIB9AUB5AEDAAVUNAEBdAVZzAECCAWt9AAAA","finger":"LEFT_INDEX_FINGER"}],"format":"ISO_19794_2","type":"FINGERPRINT_REFERENCE"},{"id":"2b9b4991-29d7-3eee-ac02-191afaa0c1a2","templates":[{"template":"Rk1SACAyMAAAAADkAAABLAGQAMUAxQEAABBjIYCzAAgQAEAhABoeAIB3ACQNAEAoACsYAEDvADYDAECrAFgDAEBQAHIaAEDUAH2AAEEEAIX4AEDhAJX4AEDpALZ7AIB1AL0RAEEJAMPuAECSAM0NAED2ANPxAEDmAOXsAEDdAPrnAEBhAQYaAIDcARDaAICwARn8AEDJASlnAECMATcVAEB7ATgbAECxAT2qAECnAT6hAEB3AT6eAECIAT6eAEEWAUDGAIDHAUizAED9AU2zAEDsAVU9AED1AWYzAIC2AWciAAAA"}],"format":"ROC_1_23","type":"FACE_REFERENCE"}]},"type":"EnrolmentRecordCreation"}]}""" @@ -45,12 +45,16 @@ class CommCareIdentityDataSourceTest { fingerIdentifier = LEFT_THUMB, templateQualityScore = 99, template = byteArrayOf(), - format = "ISO_19794_2"), + format = "ISO_19794_2" + ), FingerprintSample( fingerIdentifier = LEFT_INDEX_FINGER, templateQualityScore = 88, template = byteArrayOf(), - format = "ISO_19794_2"))), + format = "ISO_19794_2" + ) + ) + ), FingerprintIdentity( subjectId = "a961fcb4-8573-4270-a1b2-088e88275b00", fingerprints = @@ -59,25 +63,34 @@ class CommCareIdentityDataSourceTest { fingerIdentifier = LEFT_THUMB, templateQualityScore = 77, template = byteArrayOf(), - format = "ISO_19794_2"), + format = "ISO_19794_2" + ), FingerprintSample( fingerIdentifier = LEFT_INDEX_FINGER, templateQualityScore = 66, template = byteArrayOf(), - format = "ISO_19794_2"))), + format = "ISO_19794_2" + ) + ) + ), ) val expectedFaceIdentities = listOf( FaceIdentity( subjectId = "b26c91bc-b307-4131-80c3-55090ba5dbf2", - faces = listOf(FaceSample(template = byteArrayOf(), format = "ROC_1_23"))), + faces = listOf(FaceSample(template = byteArrayOf(), format = "ROC_1_23")) + ), FaceIdentity( subjectId = "a961fcb4-8573-4270-a1b2-088e88275b00", - faces = listOf(FaceSample(template = byteArrayOf(), format = "ROC_3"))), + faces = listOf(FaceSample(template = byteArrayOf(), format = "ROC_3")) + ), ) - @JvmStatic lateinit var mockMetadataUri: Uri - @JvmStatic lateinit var mockDataUri: Uri - @JvmStatic lateinit var mockDataCaseIdUri: Uri + @JvmStatic + lateinit var mockMetadataUri: Uri + @JvmStatic + lateinit var mockDataUri: Uri + @JvmStatic + lateinit var mockDataCaseIdUri: Uri @JvmStatic @BeforeClass @@ -101,11 +114,14 @@ class CommCareIdentityDataSourceTest { } } - @MockK private lateinit var encoder: EncodingUtils + @MockK + private lateinit var encoder: EncodingUtils - @MockK private lateinit var context: Context + @MockK + private lateinit var context: Context - @MockK private lateinit var mockContentResolver: ContentResolver + @MockK + private lateinit var mockContentResolver: ContentResolver private lateinit var mockMetadataCursor: Cursor @@ -120,7 +136,7 @@ class CommCareIdentityDataSourceTest { every { context.contentResolver } returns mockContentResolver every { Uri.parse(any()) } answers { - val uriPath = it.invocation.args[0] as String + val uriPath = it.invocation.args[0] as String if (uriPath.endsWith("case")) mockMetadataUri else mockDataUri } @@ -130,8 +146,8 @@ class CommCareIdentityDataSourceTest { every { mockMetadataCursor.close() } just Runs every { mockDataCursor.close() } just Runs - every { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } returns mockMetadataCursor - every { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } returns mockDataCursor + every { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } returns mockMetadataCursor + every { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } returns mockDataCursor every { encoder.base64ToBytes(any()) } returns byteArrayOf() @@ -147,9 +163,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) + listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) val query = SubjectQuery() val range = 0..expectedFingerprintIdentities.size @@ -158,18 +174,114 @@ class CommCareIdentityDataSourceTest { assertEquals(expectedFingerprintIdentities.size, actualIdentities.size) val areContentsEqual = expectedFingerprintIdentities.zip(actualIdentities) { expected, actual -> expected.subjectId == actual.subjectId && - expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> - expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && - expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && - expectedFingerprint.template.contentEquals(actualFingerprint.template) && - expectedFingerprint.format == actualFingerprint.format - } .all { it } - } .all { it } + expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> + expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && + expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && + expectedFingerprint.template.contentEquals(actualFingerprint.template) && + expectedFingerprint.format == actualFingerprint.format + }.all { it } + }.all { it } assertTrue(areContentsEqual) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } } + @Test + fun `test loadFingerprintIdentities for specific subject ID`() = runTest { + every { mockMetadataCursor.count } returns expectedFingerprintIdentities.size + every { mockMetadataCursor.moveToPosition(0) } returns true + every { mockMetadataCursor.moveToNext() } returnsMany listOf(true, false) + every { mockDataCursor.moveToNext() } returns true + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 + every { mockDataCursor.getString(0) } returnsMany + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + every { mockDataCursor.getString(1) } returnsMany + listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) + + val query = SubjectQuery(subjectId = "a961fcb4-8573-4270-a1b2-088e88275b00") + val range = 0..expectedFingerprintIdentities.size + val actualIdentities = dataSource.loadFingerprintIdentities(query, range) + + assertEquals(1, actualIdentities.size) + val areContentsEqual = expectedFingerprintIdentities + .filter { it.subjectId == "a961fcb4-8573-4270-a1b2-088e88275b00" } + .zip(actualIdentities) { expected, actual -> + expected.subjectId == actual.subjectId && + expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> + expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && + expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && + expectedFingerprint.template.contentEquals(actualFingerprint.template) && + expectedFingerprint.format == actualFingerprint.format + }.all { it } + }.all { it } + assertTrue(areContentsEqual) + } + + @Test + fun `test loadFingerprintIdentities for specific attendant ID`() = runTest { + every { mockMetadataCursor.count } returns expectedFingerprintIdentities.size + every { mockMetadataCursor.moveToPosition(0) } returns true + every { mockMetadataCursor.moveToNext() } returnsMany listOf(true, false) + every { mockDataCursor.moveToNext() } returns true + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 + every { mockDataCursor.getString(0) } returnsMany + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + every { mockDataCursor.getString(1) } returnsMany + listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) + + val query = SubjectQuery(attendantId = "r9MYXl5maWEpyKQT8AUdcPuVHUWpOkO88AdySMrjuy7uq0Dcxov3rUFIw66uXTFrKd0BnzS=") + val range = 0..expectedFingerprintIdentities.size + val actualIdentities = dataSource.loadFingerprintIdentities(query, range) + + assertEquals(1, actualIdentities.size) + val areContentsEqual = expectedFingerprintIdentities + .filter { it.subjectId == "a961fcb4-8573-4270-a1b2-088e88275b00" } + .zip(actualIdentities) { expected, actual -> + expected.subjectId == actual.subjectId && + expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> + expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && + expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && + expectedFingerprint.template.contentEquals(actualFingerprint.template) && + expectedFingerprint.format == actualFingerprint.format + }.all { it } + }.all { it } + assertTrue(areContentsEqual) + } + + @Test + fun `test loadFingerprintIdentities for specific module ID`() = runTest { + every { mockMetadataCursor.count } returns expectedFingerprintIdentities.size + every { mockMetadataCursor.moveToPosition(0) } returns true + every { mockMetadataCursor.moveToNext() } returnsMany listOf(true, false) + every { mockDataCursor.moveToNext() } returns true + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 + every { mockDataCursor.getString(0) } returnsMany + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + every { mockDataCursor.getString(1) } returnsMany + listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) + + val query = SubjectQuery(moduleId = "ePZ3yiWTt9etQAWu+A3H0WGtHI2uod==") + val range = 0..expectedFingerprintIdentities.size + val actualIdentities = dataSource.loadFingerprintIdentities(query, range) + + assertEquals(1, actualIdentities.size) + val areContentsEqual = expectedFingerprintIdentities + .filter { it.subjectId == "a961fcb4-8573-4270-a1b2-088e88275b00" } + .zip(actualIdentities) { expected, actual -> + expected.subjectId == actual.subjectId && + expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> + expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && + expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && + expectedFingerprint.template.contentEquals(actualFingerprint.template) && + expectedFingerprint.format == actualFingerprint.format + }.all { it } + }.all { it } + assertTrue(areContentsEqual) + } + @Test fun testLoadFaceIdentities() = runTest { every { mockMetadataCursor.count } returns expectedFaceIdentities.size @@ -179,9 +291,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf(SUBJECT_ACTIONS_FACE_1, SUBJECT_ACTIONS_FACE_2) + listOf(SUBJECT_ACTIONS_FACE_1, SUBJECT_ACTIONS_FACE_2) val query = SubjectQuery() val range = 0..expectedFaceIdentities.size @@ -190,10 +302,10 @@ class CommCareIdentityDataSourceTest { assertEquals(expectedFaceIdentities.size, actualIdentities.size) val areContentsEqual = expectedFaceIdentities.zip(actualIdentities) { expected, actual -> expected.subjectId == actual.subjectId && - expected.faces.zip(actual.faces) { expectedFace, actualFace -> - expectedFace.template.contentEquals(actualFace.template) && - expectedFace.format == actualFace.format - }.all { it} + expected.faces.zip(actual.faces) { expectedFace, actualFace -> + expectedFace.template.contentEquals(actualFace.template) && + expectedFace.format == actualFace.format + }.all { it } }.all { it } assertTrue(areContentsEqual) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } @@ -209,9 +321,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2, SUBJECT_ACTIONS_FACE_1) + listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2, SUBJECT_ACTIONS_FACE_1) val query = SubjectQuery() val range = 0..expectedFingerprintIdentities.size @@ -220,13 +332,13 @@ class CommCareIdentityDataSourceTest { assertEquals(expectedFingerprintIdentities.size, actualIdentities.size) val areContentsEqual = expectedFingerprintIdentities.zip(actualIdentities) { expected, actual -> expected.subjectId == actual.subjectId && - expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> - expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && - expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && - expectedFingerprint.template.contentEquals(actualFingerprint.template) && - expectedFingerprint.format == actualFingerprint.format - } .all { it } - } .all { it } + expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> + expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && + expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && + expectedFingerprint.template.contentEquals(actualFingerprint.template) && + expectedFingerprint.format == actualFingerprint.format + }.all { it } + }.all { it } assertTrue(areContentsEqual) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } @@ -241,9 +353,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf(SUBJECT_ACTIONS_FACE_1, SUBJECT_ACTIONS_FACE_2, SUBJECT_ACTIONS_FINGERPRINT_1) + listOf(SUBJECT_ACTIONS_FACE_1, SUBJECT_ACTIONS_FACE_2, SUBJECT_ACTIONS_FINGERPRINT_1) val query = SubjectQuery() val range = 0..expectedFaceIdentities.size @@ -252,10 +364,10 @@ class CommCareIdentityDataSourceTest { assertEquals(expectedFaceIdentities.size, actualIdentities.size) val areContentsEqual = expectedFaceIdentities.zip(actualIdentities) { expected, actual -> expected.subjectId == actual.subjectId && - expected.faces.zip(actual.faces) { expectedFace, actualFace -> - expectedFace.template.contentEquals(actualFace.template) && - expectedFace.format == actualFace.format - }.all { it} + expected.faces.zip(actual.faces) { expectedFace, actualFace -> + expectedFace.template.contentEquals(actualFace.template) && + expectedFace.format == actualFace.format + }.all { it } }.all { it } assertTrue(areContentsEqual) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } @@ -271,9 +383,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf(SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_1, SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_2) + listOf(SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_1, SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_2) val query = SubjectQuery() val range = 0..expectedFingerprintIdentities.size @@ -282,13 +394,13 @@ class CommCareIdentityDataSourceTest { assertEquals(expectedFingerprintIdentities.size, actualIdentities.size) val areContentsEqual = expectedFingerprintIdentities.zip(actualIdentities) { expected, actual -> expected.subjectId == actual.subjectId && - expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> - expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && - expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && - expectedFingerprint.template.contentEquals(actualFingerprint.template) && - expectedFingerprint.format == actualFingerprint.format - } .all { it } - } .all { it } + expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> + expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && + expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && + expectedFingerprint.template.contentEquals(actualFingerprint.template) && + expectedFingerprint.format == actualFingerprint.format + }.all { it } + }.all { it } assertTrue(areContentsEqual) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } @@ -303,9 +415,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf(SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_1, SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_2) + listOf(SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_1, SUBJECT_ACTIONS_FINGERPRINT_AND_FACE_2) val query = SubjectQuery() val range = 0..expectedFaceIdentities.size @@ -314,10 +426,10 @@ class CommCareIdentityDataSourceTest { assertEquals(expectedFaceIdentities.size, actualIdentities.size) val areContentsEqual = expectedFaceIdentities.zip(actualIdentities) { expected, actual -> expected.subjectId == actual.subjectId && - expected.faces.zip(actual.faces) { expectedFace, actualFace -> - expectedFace.template.contentEquals(actualFace.template) && - expectedFace.format == actualFace.format - }.all { it} + expected.faces.zip(actual.faces) { expectedFace, actualFace -> + expectedFace.template.contentEquals(actualFace.template) && + expectedFace.format == actualFace.format + }.all { it } }.all { it } assertTrue(areContentsEqual) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } @@ -373,9 +485,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) + listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) val query = SubjectQuery() val range = 0..expectedFingerprintIdentities.size @@ -384,12 +496,12 @@ class CommCareIdentityDataSourceTest { assertEquals(expectedFingerprintIdentities.size, actualIdentities.size) val areContentsEqual = expectedFingerprintIdentities.zip(actualIdentities) { expected, actual -> expected.subjectId == actual.subjectId && - expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> - expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && - expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && - expectedFingerprint.template.contentEquals(actualFingerprint.template) && - expectedFingerprint.format == actualFingerprint.format - }.all { it } + expected.fingerprints.zip(actual.fingerprints) { expectedFingerprint, actualFingerprint -> + expectedFingerprint.fingerIdentifier == actualFingerprint.fingerIdentifier && + expectedFingerprint.templateQualityScore == actualFingerprint.templateQualityScore && + expectedFingerprint.template.contentEquals(actualFingerprint.template) && + expectedFingerprint.format == actualFingerprint.format + }.all { it } }.all { it } assertTrue(areContentsEqual) coVerify { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } @@ -416,7 +528,7 @@ class CommCareIdentityDataSourceTest { @Test fun `exception during metadata cursor access is reported`() = runTest { every { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } throws - RuntimeException("Some exception") + RuntimeException("Some exception") val query = SubjectQuery() val range = 0..2 @@ -473,9 +585,9 @@ class CommCareIdentityDataSourceTest { every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 every { mockDataCursor.getString(0) } returnsMany - listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") every { mockDataCursor.getString(1) } returnsMany - listOf("invalid JSON 1", "invalid JSON 2") + listOf("invalid JSON 1", "invalid JSON 2") val query = SubjectQuery() val range = 0..2 From 369af42ef891bce5450f14cdff88a7d4e45afb84 Mon Sep 17 00:00:00 2001 From: Sergejs Luhmirins Date: Wed, 28 Aug 2024 16:25:35 +0300 Subject: [PATCH 2/2] MS-276 Pass metadata in request action to allow arbitrary data from caller app --- .../builders/ConfirmIdentifyRequestBuilder.kt | 1 + .../ConfirmIdentityActionFactory.kt | 1 + .../logincheck/usecases/ActionFactory.kt | 1 + .../steps/BuildMatcherSubjectQueryUseCase.kt | 9 +++++--- .../usecases/ShouldCreatePersonUseCaseTest.kt | 1 + .../commcare/CommCareIdentityDataSource.kt | 20 +++++++++++++++-- .../store/domain/models/SubjectQuery.kt | 3 ++- .../CommCareIdentityDataSourceTest.kt | 20 +++++++++++++++++ .../infra/orchestration/data/ActionRequest.kt | 22 ++++++++++--------- 9 files changed, 62 insertions(+), 16 deletions(-) diff --git a/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/request/builders/ConfirmIdentifyRequestBuilder.kt b/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/request/builders/ConfirmIdentifyRequestBuilder.kt index 0ed436e822..b5deaf91f1 100644 --- a/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/request/builders/ConfirmIdentifyRequestBuilder.kt +++ b/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/request/builders/ConfirmIdentifyRequestBuilder.kt @@ -36,6 +36,7 @@ internal class ConfirmIdentifyRequestBuilder( userId = extractor.getUserId().asTokenizableRaw(), sessionId = extractor.getSessionId(), selectedGuid = extractor.getSelectedGuid(), + metadata = extractor.getMetadata(), unknownExtras = extractor.getUnknownExtras(), ) } diff --git a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/request/requestFactories/ConfirmIdentityActionFactory.kt b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/request/requestFactories/ConfirmIdentityActionFactory.kt index b30693036f..cbfe7863a5 100644 --- a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/request/requestFactories/ConfirmIdentityActionFactory.kt +++ b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/request/requestFactories/ConfirmIdentityActionFactory.kt @@ -24,6 +24,7 @@ internal object ConfirmIdentityActionFactory : RequestActionFactory() { userId = MOCK_USER_ID.asTokenizableRaw(), sessionId = MOCK_SESSION_ID, selectedGuid = MOCK_SELECTED_GUID, + metadata = MOCK_METADATA, unknownExtras = emptyMap() ) diff --git a/feature/login-check/src/test/java/com/simprints/feature/logincheck/usecases/ActionFactory.kt b/feature/login-check/src/test/java/com/simprints/feature/logincheck/usecases/ActionFactory.kt index 54e1a9724b..5d3b132977 100644 --- a/feature/login-check/src/test/java/com/simprints/feature/logincheck/usecases/ActionFactory.kt +++ b/feature/login-check/src/test/java/com/simprints/feature/logincheck/usecases/ActionFactory.kt @@ -32,6 +32,7 @@ internal object ActionFactory { userId = MOCK_USER_ID, sessionId = "sessionId", selectedGuid = "selectedGuid", + metadata = "", unknownExtras = emptyMap() ) 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 592a60577e..5f79161fee 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 @@ -15,17 +15,20 @@ internal class BuildMatcherSubjectQueryUseCase @Inject constructor() { actionRequest: ActionRequest, ) = when (projectConfiguration.identification.poolType) { IdentificationConfiguration.PoolType.PROJECT -> SubjectQuery( - projectId = actionRequest.projectId + projectId = actionRequest.projectId, + metadata = actionRequest.metadata, ) IdentificationConfiguration.PoolType.USER -> SubjectQuery( projectId = actionRequest.projectId, - attendantId = actionRequest.userId.value + attendantId = actionRequest.userId.value, + metadata = actionRequest.metadata, ) IdentificationConfiguration.PoolType.MODULE -> SubjectQuery( projectId = actionRequest.projectId, - moduleId = (actionRequest as ActionRequest.FlowAction).moduleId.value + moduleId = (actionRequest as ActionRequest.FlowAction).moduleId.value, + metadata = actionRequest.metadata, ) } } diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/ShouldCreatePersonUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/ShouldCreatePersonUseCaseTest.kt index 6a6abf8582..14899da5c0 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/ShouldCreatePersonUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/ShouldCreatePersonUseCaseTest.kt @@ -45,6 +45,7 @@ class ShouldCreatePersonUseCaseTest { userId = "".asTokenizableRaw(), sessionId = "", selectedGuid = "", + metadata = "", unknownExtras = emptyMap(), ), modalities = emptySet(), results = emptyList() ) diff --git a/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt b/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt index b2202c2a29..7bf6339956 100644 --- a/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt +++ b/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSource.kt @@ -19,12 +19,12 @@ import com.simprints.infra.enrolment.records.store.domain.models.FingerprintIden import com.simprints.infra.enrolment.records.store.domain.models.SubjectQuery import com.simprints.infra.events.event.cosync.CoSyncEnrolmentRecordEvents import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordCreationEvent -import com.simprints.infra.events.event.domain.models.subject.EnrolmentRecordEvent import com.simprints.infra.events.event.domain.models.subject.FaceReference import com.simprints.infra.events.event.domain.models.subject.FingerprintReference import com.simprints.infra.logging.Simber import com.simprints.libsimprints.Constants.SIMPRINTS_COSYNC_SUBJECT_ACTIONS import dagger.hilt.android.qualifiers.ApplicationContext +import org.json.JSONException import javax.inject.Inject internal class CommCareIdentityDataSource @Inject constructor( @@ -38,6 +38,8 @@ internal class CommCareIdentityDataSource @Inject constructor( const val COLUMN_CASE_ID = "case_id" const val COLUMN_DATUM_ID = "datum_id" const val COLUMN_VALUE = "value" + + const val ARG_CASE_ID = "caseId" } private fun getCaseMetadataUri(packageName: String): Uri = @@ -75,8 +77,12 @@ internal class CommCareIdentityDataSource @Inject constructor( query: SubjectQuery, ): List { val enrolmentRecordCreationEvents: MutableList = mutableListOf() - try { + val caseId = attemptExtractingCaseId(query.metadata) + if (caseId != null) { + return loadEnrolmentRecordCreationEvents(caseId, callerPackageName, query) + } + context.contentResolver.query( getCaseMetadataUri(callerPackageName), null, null, null, null )?.use { caseMetadataCursor -> @@ -95,6 +101,16 @@ internal class CommCareIdentityDataSource @Inject constructor( return enrolmentRecordCreationEvents } + private fun attemptExtractingCaseId(metadata: String?) = metadata + ?.takeUnless { it.isEmpty() } + ?.let { + try { + JsonHelper.fromJson>(it)[ARG_CASE_ID] as? String + } catch (_: JSONException) { + null + } + } + override suspend fun loadFaceIdentities( query: SubjectQuery, range: IntRange, diff --git a/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/domain/models/SubjectQuery.kt b/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/domain/models/SubjectQuery.kt index 8f75acf699..48190d4950 100644 --- a/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/domain/models/SubjectQuery.kt +++ b/infra/enrolment-records-store/src/main/java/com/simprints/infra/enrolment/records/store/domain/models/SubjectQuery.kt @@ -14,5 +14,6 @@ data class SubjectQuery( val hasUntokenizedFields: Boolean? = null, val moduleId: String? = null, val sort: Boolean = false, - val afterSubjectId: String? = null + val afterSubjectId: String? = null, + val metadata: String? = null, ) : Serializable diff --git a/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt b/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt index 4e10d723f2..ddfce3d491 100644 --- a/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt +++ b/infra/enrolment-records-store/src/test/java/com/simprints/infra/enrolment/records/store/commcare/CommCareIdentityDataSourceTest.kt @@ -87,8 +87,10 @@ class CommCareIdentityDataSourceTest { @JvmStatic lateinit var mockMetadataUri: Uri + @JvmStatic lateinit var mockDataUri: Uri + @JvmStatic lateinit var mockDataCaseIdUri: Uri @@ -282,6 +284,24 @@ class CommCareIdentityDataSourceTest { assertTrue(areContentsEqual) } + @Test + fun `test load fingerprint fetches case by providedID`() = runTest { + every { mockDataCursor.moveToNext() } returns true + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_DATUM_ID) } returns 0 + every { mockDataCursor.getColumnIndexOrThrow(COLUMN_VALUE) } returns 1 + every { mockDataCursor.getString(0) } returnsMany + listOf("someOtherDatumId", "subjectActions", "someOtherDatumId", "subjectActions") + every { mockDataCursor.getString(1) } returnsMany + listOf(SUBJECT_ACTIONS_FINGERPRINT_1, SUBJECT_ACTIONS_FINGERPRINT_2) + + val query = SubjectQuery(metadata = """{"caseId":"CASE"}""") + val range = 0..1 + dataSource.loadFingerprintIdentities(query, range) + + coVerify(exactly = 0) { mockContentResolver.query(mockMetadataUri, any(), any(), any(), any()) } + coVerify { mockContentResolver.query(mockDataCaseIdUri, any(), any(), any(), any()) } + } + @Test fun testLoadFaceIdentities() = runTest { every { mockMetadataCursor.count } returns expectedFaceIdentities.size diff --git a/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionRequest.kt b/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionRequest.kt index 09d2ec0775..f1792ba932 100644 --- a/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionRequest.kt +++ b/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionRequest.kt @@ -24,6 +24,7 @@ sealed class ActionRequest( open val actionIdentifier: ActionRequestIdentifier, open val projectId: String, open val userId: TokenizableString, + open val metadata: String, open val unknownExtras: Map, ) : Serializable { @@ -31,7 +32,7 @@ sealed class ActionRequest( when (this) { is EnrolActionRequest -> subjectAge is IdentifyActionRequest -> subjectAge - is VerifyActionRequest ->subjectAge + is VerifyActionRequest -> subjectAge else -> null } @@ -44,9 +45,9 @@ sealed class ActionRequest( val biometricDataSource: String, val subjectAge: Int? = null, val callerPackageName: String, - val metadata: String, + override val metadata: String, override val unknownExtras: Map, - ) : ActionRequest(actionIdentifier, projectId, userId, unknownExtras), FlowAction + ) : ActionRequest(actionIdentifier, projectId, userId, metadata, unknownExtras), FlowAction @Keep data class IdentifyActionRequest( @@ -57,9 +58,9 @@ sealed class ActionRequest( val biometricDataSource: String, val subjectAge: Int? = null, val callerPackageName: String, - val metadata: String, + override val metadata: String, override val unknownExtras: Map, - ) : ActionRequest(actionIdentifier, projectId, userId, unknownExtras), FlowAction + ) : ActionRequest(actionIdentifier, projectId, userId, metadata, unknownExtras), FlowAction @Keep data class VerifyActionRequest( @@ -70,10 +71,10 @@ sealed class ActionRequest( val biometricDataSource: String, val subjectAge: Int? = null, val callerPackageName: String, - val metadata: String, val verifyGuid: String, + override val metadata: String, override val unknownExtras: Map, - ) : ActionRequest(actionIdentifier, projectId, userId, unknownExtras), FlowAction + ) : ActionRequest(actionIdentifier, projectId, userId, metadata, unknownExtras), FlowAction @Keep data class ConfirmIdentityActionRequest( @@ -82,8 +83,9 @@ sealed class ActionRequest( override val userId: TokenizableString, val sessionId: String, val selectedGuid: String, + override val metadata: String, override val unknownExtras: Map, - ) : ActionRequest(actionIdentifier, projectId, userId, unknownExtras), FollowUpAction + ) : ActionRequest(actionIdentifier, projectId, userId, metadata, unknownExtras), FollowUpAction @Keep data class EnrolLastBiometricActionRequest( @@ -91,10 +93,10 @@ sealed class ActionRequest( override val projectId: String, override val userId: TokenizableString, val moduleId: TokenizableString, - val metadata: String, val sessionId: String, + override val metadata: String, override val unknownExtras: Map, - ) : ActionRequest(actionIdentifier, projectId, userId, unknownExtras), FollowUpAction + ) : ActionRequest(actionIdentifier, projectId, userId, metadata, unknownExtras), FollowUpAction interface FlowAction { val moduleId: TokenizableString