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 62104e8926..3ea57cd203 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 @@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope import com.simprints.core.livedata.LiveDataEvent import com.simprints.core.livedata.LiveDataEventWithContent import com.simprints.core.livedata.send +import com.simprints.core.tools.extentions.isValidGuid import com.simprints.core.tools.extentions.toJsonElementMap import com.simprints.core.tools.time.TimeHelper import com.simprints.feature.clientapi.exceptions.InvalidRequestException @@ -153,6 +154,19 @@ class ClientApiViewModel @Inject internal constructor( val currentSessionId = getCurrentSessionId() simpleEventReporter.addCompletionCheckEvent(flowCompleted = true) + val selectedGuid = (action as? ActionRequest.ConfirmIdentityActionRequest) + ?.selectedGuid + ?.takeIf { it.isValidGuid() } + val coSyncEnrolmentRecords = if ( + confirmResponse.identificationOutcome && + confirmResponse.externalCredential != null && + selectedGuid != null + ) { + getEnrolmentCreationEventForRecord(action.projectId, selectedGuid) + } else { + null + } + deleteSessionEventsIfNeeded(currentSessionId) logIntent(action, currentSessionId, "Confirmed: ${confirmResponse.identificationOutcome}") @@ -163,6 +177,7 @@ class ClientApiViewModel @Inject internal constructor( actionIdentifier = action.actionIdentifier, sessionId = currentSessionId, confirmed = confirmResponse.identificationOutcome, + subjectActions = coSyncEnrolmentRecords, externalCredential = confirmResponse.externalCredential, ), ), diff --git a/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapper.kt b/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapper.kt index 38b79a38b3..adde241340 100644 --- a/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapper.kt +++ b/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapper.kt @@ -45,7 +45,7 @@ internal class CommCareResponseMapper @Inject constructor( CommCareConstants.COMMCARE_SID_VERSION to appVersionName, CommCareConstants.SIMPRINTS_SESSION_ID to response.sessionId, CommCareConstants.BIOMETRICS_COMPLETE_CHECK_KEY to "true", - ).toCommCareBundle() + ).appendCoSyncData(response.subjectActions).toCommCareBundle() is ActionResponse.VerifyActionResponse -> bundleOf( CommCareConstants.COMMCARE_DEVICE_ID to deviceId, diff --git a/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapper.kt b/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapper.kt index 1e20b37a28..e4ae122dbd 100644 --- a/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapper.kt +++ b/feature/client-api/src/main/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapper.kt @@ -88,7 +88,8 @@ internal class LibSimprintsResponseMapper @Inject constructor( Constants.SIMPRINTS_APP_VERSION_NAME to appVersionName, Constants.SIMPRINTS_BIOMETRICS_COMPLETE_CHECK to true, Constants.SIMPRINTS_HAS_CREDENTIAL to (response.externalCredential != null), - ).appendExternalCredential(response.externalCredential) + ).appendCoSyncData(response.subjectActions) + .appendExternalCredential(response.externalCredential) } is ActionResponse.VerifyActionResponse -> bundleOf( 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 5b7ed1b6c2..c04679d7dd 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 @@ -201,7 +201,7 @@ internal class ClientApiViewModelTest { @Test fun `handleConfirmResponse saves correct events`() = runTest { viewModel.handleConfirmResponse( - mockRequest(), + mockConfirmRequest(), mockk { every { identificationOutcome } returns true every { externalCredential } returns mockk() @@ -210,6 +210,7 @@ internal class ClientApiViewModelTest { coVerify { simpleEventReporter.addCompletionCheckEvent(eq(true)) + getEnrolmentCreationEventForRecord.invoke("projectId", SELECTED_GUID) deleteSessionEventsIfNeeded(any()) persistentLogger.log(any(), any(), any(), any()) } @@ -217,6 +218,57 @@ internal class ClientApiViewModelTest { viewModel.returnResponse.test().assertHasValue() } + @Test + fun `handleConfirmResponse does not generate cosync when no credential`() = runTest { + viewModel.handleConfirmResponse( + mockConfirmRequest(), + mockk { + every { identificationOutcome } returns true + every { externalCredential } returns null + }, + ) + + coVerify(exactly = 0) { + getEnrolmentCreationEventForRecord.invoke(any(), any()) + } + verify { resultMapper.invoke(match { it is ActionResponse.ConfirmActionResponse }) } + viewModel.returnResponse.test().assertHasValue() + } + + @Test + fun `handleConfirmResponse does not generate cosync when identification outcome is false`() = runTest { + viewModel.handleConfirmResponse( + mockConfirmRequest(), + mockk { + every { identificationOutcome } returns false + every { externalCredential } returns mockk() + }, + ) + + coVerify(exactly = 0) { + getEnrolmentCreationEventForRecord.invoke(any(), any()) + } + verify { resultMapper.invoke(match { it is ActionResponse.ConfirmActionResponse }) } + viewModel.returnResponse.test().assertHasValue() + } + + @Test + fun `handleConfirmResponse does not generate cosync when no subject selected`() = runTest { + viewModel.handleConfirmResponse( + mockConfirmRequest(guid = NONE_SELECTED), + mockk { + every { identificationOutcome } returns true + every { externalCredential } returns mockk() + }, + ) + + coVerify(exactly = 0) { + getEnrolmentCreationEventForRecord.invoke(any(), any()) + } + verify { resultMapper.invoke(match { it is ActionResponse.ConfirmActionResponse }) } + viewModel.returnResponse.test().assertHasValue() + } + @Test fun `handleVerifyResponse saves correct events`() = runTest { viewModel.handleVerifyResponse( @@ -308,4 +360,15 @@ internal class ClientApiViewModelTest { every { projectId } returns "projectId" every { actionIdentifier } returns ActionRequestIdentifier("action", "package", "", 1, 0L) } + + private fun mockConfirmRequest(guid: String = SELECTED_GUID): ActionRequest.ConfirmIdentityActionRequest = mockk { + every { projectId } returns "projectId" + every { selectedGuid } returns guid + every { actionIdentifier } returns ActionRequestIdentifier("action", "package", "", 1, 0L) + } + + companion object { + private const val SELECTED_GUID = "0b7a26c1-0d4d-4e1b-9c50-1cb3d1e0d1f2" + private const val NONE_SELECTED = "NONE_SELECTED" + } } diff --git a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/ActionToIntentMapperTest.kt b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/ActionToIntentMapperTest.kt index 1a39853093..2de568fb00 100644 --- a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/ActionToIntentMapperTest.kt +++ b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/ActionToIntentMapperTest.kt @@ -73,6 +73,7 @@ class ActionToIntentMapperTest { actionIdentifier = ConfirmIdentityActionFactory.getIdentifier().copy(packageName = packageName), sessionId = "sessionId", confirmed = true, + subjectActions = null, externalCredential = null, ) } diff --git a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapperTest.kt b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapperTest.kt index 53f615d29c..b309c56e11 100644 --- a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapperTest.kt +++ b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/CommCareResponseMapperTest.kt @@ -88,6 +88,7 @@ class CommCareResponseMapperTest { actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(), sessionId = "sessionId", confirmed = true, + subjectActions = null, externalCredential = null, ), ).getBundle(CommCareConstants.COMMCARE_BUNDLE_KEY) ?: bundleOf() @@ -98,6 +99,21 @@ class CommCareResponseMapperTest { assertThat(extras.getString(CommCareConstants.BIOMETRICS_COMPLETE_CHECK_KEY)).isEqualTo("true") } + @Test + fun `correctly maps confirm response with cosync data`() { + val extras = mapper( + ActionResponse.ConfirmActionResponse( + actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(), + sessionId = "sessionId", + confirmed = true, + subjectActions = "cosyncJson", + externalCredential = null, + ), + ).getBundle(CommCareConstants.COMMCARE_BUNDLE_KEY) ?: bundleOf() + + assertThat(extras.getString(Constants.SIMPRINTS_COSYNC_SUBJECT_ACTIONS)).isEqualTo("cosyncJson") + } + @Test fun `correctly maps verify response with null verificationSuccess`() { val extras = mapper( diff --git a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapperTest.kt b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapperTest.kt index ff285868b6..9eebb464e9 100644 --- a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapperTest.kt +++ b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/LibSimprintsResponseMapperTest.kt @@ -146,6 +146,7 @@ class LibSimprintsResponseMapperTest { actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(), sessionId = "sessionId", confirmed = true, + subjectActions = null, externalCredential = mockk { every { value } returns expectedValue every { type } returns expectedType @@ -162,6 +163,21 @@ class LibSimprintsResponseMapperTest { assertThat(extras.getString(Constants.SIMPRINTS_SCANNED_CREDENTIAL)).isEqualTo(expectedJson) } + @Test + fun `correctly maps confirm response with cosync data`() { + val extras = mapper( + ActionResponse.ConfirmActionResponse( + actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(), + sessionId = "sessionId", + confirmed = true, + subjectActions = "cosyncJson", + externalCredential = null, + ), + ) + + assertThat(extras.getString(Constants.SIMPRINTS_COSYNC_SUBJECT_ACTIONS)).isEqualTo("cosyncJson") + } + @Test fun `correctly maps verify response with null verificationSuccess`() { val extras = mapper( @@ -435,6 +451,7 @@ class LibSimprintsResponseMapperTest { actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(), sessionId = "sessionId", confirmed = true, + subjectActions = null, externalCredential = null, ), ) diff --git a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/OdkResponseMapperTest.kt b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/OdkResponseMapperTest.kt index 2459de0d2e..66a1680f31 100644 --- a/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/OdkResponseMapperTest.kt +++ b/feature/client-api/src/test/java/com/simprints/feature/clientapi/mappers/response/OdkResponseMapperTest.kt @@ -90,6 +90,7 @@ class OdkResponseMapperTest { actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(), sessionId = "sessionId", confirmed = true, + subjectActions = null, externalCredential = null, ), ) diff --git a/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionResponse.kt b/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionResponse.kt index 59f746c8ff..a6951f5ca3 100644 --- a/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionResponse.kt +++ b/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/ActionResponse.kt @@ -33,6 +33,7 @@ sealed class ActionResponse( override val actionIdentifier: ActionRequestIdentifier, override val sessionId: String, val confirmed: Boolean, + val subjectActions: String?, val externalCredential: AppExternalCredential?, ) : ActionResponse(actionIdentifier, sessionId)