Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion feature/client-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("simprints.feature")
id("kotlin-parcelize")
id("simprints.library.kotlinSerialization")
}
android {
namespace = "com.simprints.feature.clientapi"
Expand All @@ -15,5 +16,4 @@ dependencies {
implementation(project(":infra:logging-persistent"))

implementation(libs.libsimprints)
implementation(libs.jackson.core)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.simprints.feature.clientapi.mappers.request.extractors

import android.content.Intent
import com.simprints.core.tools.json.JsonHelper
import com.simprints.feature.clientapi.extensions.extractString
import com.simprints.feature.clientapi.models.ClientApiConstants
import com.simprints.libsimprints.Constants
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonPrimitive

internal abstract class ActionRequestExtractor(
private val extras: Map<String, Any>,
Expand All @@ -30,14 +32,15 @@ internal abstract class ActionRequestExtractor(

open fun getMetadata(): String = extras.extractString(Constants.SIMPRINTS_METADATA)

open fun getSubjectAge(): Int? = try {
val parsedMetadata = JsonHelper.fromJson<Map<String, Any>>(getMetadata())
parsedMetadata[Constants.SIMPRINTS_SUBJECT_AGE] as? Int
} catch (e: Exception) {
fun getSubjectAge(): Int? = try {
val parsedMetadata =
JsonHelper.json.decodeFromString<Map<String, JsonElement>>(getMetadata())
parsedMetadata[Constants.SIMPRINTS_SUBJECT_AGE]
?.jsonPrimitive
?.intOrNull
} catch (_: Exception) {
null
}

protected open fun Intent.extractString(key: String): String = this.getStringExtra(key) ?: ""

open fun getUnknownExtras(): Map<String, Any?> = extras.filter { it.key.isNotBlank() && !expectedKeys.contains(it.key) }
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package com.simprints.feature.clientapi.usecases

import com.fasterxml.jackson.databind.module.SimpleModule
import com.simprints.core.domain.tokenization.TokenizableString
import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameDeserializer
import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameSerializer
import com.simprints.core.tools.json.JsonHelper
import com.simprints.core.tools.utils.EncodingUtils
import com.simprints.infra.config.store.ConfigRepository
Expand Down Expand Up @@ -46,7 +42,7 @@ internal class GetEnrolmentCreationEventForRecordUseCase @Inject constructor(
return null
}

return jsonHelper.toJson(CoSyncEnrolmentRecordEvents(listOf(recordCreationEvent)), coSyncSerializationModule)
return jsonHelper.json.encodeToString(CoSyncEnrolmentRecordEvents(listOf(recordCreationEvent)))
}

private fun EnrolmentRecord.fromSubjectToEnrolmentCreationEvent() = EnrolmentRecordCreationEvent(
Expand All @@ -57,11 +53,4 @@ internal class GetEnrolmentCreationEventForRecordUseCase @Inject constructor(
biometricReferences = EnrolmentRecordCreationEvent.buildBiometricReferences(references, encoder),
externalCredentials = externalCredentials,
)

companion object {
val coSyncSerializationModule = SimpleModule().apply {
addSerializer(TokenizableString::class.java, TokenizationClassNameSerializer())
addDeserializer(TokenizableString::class.java, TokenizationClassNameDeserializer())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.simprints.feature.clientapi.mappers.request.extractors

import com.google.common.truth.Truth.assertThat
import com.simprints.libsimprints.Constants.SIMPRINTS_METADATA
import com.simprints.libsimprints.Constants.SIMPRINTS_SUBJECT_AGE
import kotlin.test.Test

internal class ActionRequestExtractorTest {
// Concrete subclass for testing
class TestActionRequestExtractor(
extras: Map<String, Any>,
override val expectedKeys: List<String> = emptyList(),
) : ActionRequestExtractor(extras)

@Test
fun `returns age when valid integer present`() {
val extractor = TestActionRequestExtractor(
mapOf(SIMPRINTS_METADATA to """{"$SIMPRINTS_SUBJECT_AGE": 30}"""),
)
assertThat(extractor.getSubjectAge()).isEqualTo(30)
}

@Test
fun `returns null when age key is missing`() {
val extractor = TestActionRequestExtractor(
mapOf(SIMPRINTS_METADATA to """{"name": "Alice"}"""),
)
assertThat(extractor.getSubjectAge()).isNull()
}

@Test
fun `returns null when age key is not an integer`() {
val extractor = TestActionRequestExtractor(
mapOf(SIMPRINTS_METADATA to """{"$SIMPRINTS_SUBJECT_AGE": "twenty"}"""),
)
assertThat(extractor.getSubjectAge()).isNull()
}

@Test
fun `returns null for invalid JSON`() {
val extractor = TestActionRequestExtractor(
mapOf(SIMPRINTS_METADATA to """{invalid json"""),
)
assertThat(extractor.getSubjectAge()).isNull()
}

@Test
fun `returns null when age is null in JSON`() {
val extractor = TestActionRequestExtractor(
mapOf(SIMPRINTS_METADATA to """{"$SIMPRINTS_SUBJECT_AGE": null}"""),
)
assertThat(extractor.getSubjectAge()).isNull()
}

@Test
fun `returns null when metadata key is missing`() {
val extractor = TestActionRequestExtractor(
emptyMap(),
)
assertThat(extractor.getSubjectAge()).isNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.simprints.core.tools.utils.EncodingUtils
import com.simprints.infra.config.store.ConfigRepository
import com.simprints.infra.config.store.models.UpSynchronizationConfiguration
import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository
import com.simprints.infra.events.event.cosync.CoSyncEnrolmentRecordEvents
import com.simprints.testtools.common.coroutines.TestCoroutineRule
import io.mockk.*
import io.mockk.impl.annotations.MockK
Expand Down Expand Up @@ -94,7 +95,7 @@ class GetEnrolmentCreationEventForRecordUseCaseTest {
val result = useCase("projectId", "subjectId")

coVerify { enrolmentRecordRepository.load(any()) }
coVerify { jsonHelper.toJson(any(), any()) }
coVerify { jsonHelper.json.encodeToString(any<CoSyncEnrolmentRecordEvents>()) }
assertThat(result).isNotNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.Module
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json

object JsonHelper {
Expand Down Expand Up @@ -59,8 +60,11 @@ object JsonHelper {

inline fun <reified T> fromJson(json: String): T = jackson.readValue(json, T::class.java)

fun validateJsonOrThrow(json: String) {
jackson.readTree(json)
/**
* @throws SerializationException if the JSON is not valid
*/
fun validateJsonOrThrow(jsonString: String) {
json.parseToJsonElement(jsonString)
}

// Todo will be replacing the above fromJson and toJson once completely removing jackson before the 2026.1.0 release
Expand Down