Coverage Summary for Class: MindboxWebViewOperationExecutor (cloud.mindbox.mobile_sdk.inapp.presentation.view)

Class Method, % Branch, % Line, % Instruction, %
MindboxWebViewOperationExecutor 100% (6/6) 100% (22/22) 100% (38/38) 100% (226/226)
MindboxWebViewOperationExecutor$Companion
MindboxWebViewOperationExecutor$executeSyncOperation$2$1 100% (1/1) 50% (1/2) 100% (2/2) 100% (12/12)
MindboxWebViewOperationExecutor$executeSyncOperation$2$2 100% (1/1) 50% (1/2) 100% (3/3) 100% (22/22)
Total 100% (8/8) 92.3% (24/26) 100% (43/43) 100% (260/260)


 package cloud.mindbox.mobile_sdk.inapp.presentation.view
 
 import android.app.Application
 import cloud.mindbox.mobile_sdk.logger.mindboxLogW
 import cloud.mindbox.mobile_sdk.managers.MindboxEventManager
 import cloud.mindbox.mobile_sdk.models.MindboxError
 import com.google.gson.Gson
 import com.google.gson.JsonElement
 import com.google.gson.JsonObject
 import com.google.gson.JsonParser
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 import kotlin.coroutines.resumeWithException
 
 internal interface WebViewOperationExecutor {
 
     fun executeAsyncOperation(context: Application, payload: String?, tags: Map<String, String>?)
 
     suspend fun executeSyncOperation(payload: String?, tags: Map<String, String>?): String
 }
 
 internal class MindboxWebViewOperationExecutor(
     private val gson: Gson,
 ) : WebViewOperationExecutor {
 
     companion object {
         private const val OPERATION_FIELD = "operation"
         private const val BODY_FIELD = "body"
         private const val TAGS_FIELD = "tags"
     }
 
     override fun executeAsyncOperation(context: Application, payload: String?, tags: Map<String, String>?) {
         val (operation, body) = parseOperationRequest(payload, tags)
         MindboxEventManager.asyncOperation(
             context = context,
             name = operation,
             body = body,
         )
     }
 
     override suspend fun executeSyncOperation(payload: String?, tags: Map<String, String>?): String {
         val (operation, body) = parseOperationRequest(payload, tags)
         return suspendCancellableCoroutine { continuation ->
             MindboxEventManager.syncOperation(
                 name = operation,
                 bodyJson = body,
                 onSuccess = { responseBody: String ->
                     if (continuation.isActive) {
                         continuation.resume(responseBody)
                     }
                 },
                 onError = { error: MindboxError ->
                     if (continuation.isActive) {
                         continuation.resumeWithException(
                             WebViewSyncOperationException(error.toWebViewDataJson(gson))
                         )
                     }
                 },
             )
         }
     }
 
     private fun parseOperationRequest(payload: String?, tags: Map<String, String>?): Pair<String, String> {
         payload ?: throw IllegalArgumentException("Payload is not provided")
         val jsonObject: JsonObject = runCatching { JsonParser.parseString(payload).asJsonObject }
             .getOrElse { throw IllegalArgumentException("Payload is not a valid JSON object", it) }
         val operation: String = jsonObject.getAsJsonPrimitive(OPERATION_FIELD)?.asString
             ?: throw IllegalArgumentException("Operation is not provided")
         val bodyObject: JsonObject = jsonObject.getAsJsonObject(BODY_FIELD)
             ?: throw IllegalArgumentException("Body is not provided")
         return operation to buildOperationBody(bodyObject, tags)
     }
 
     private fun buildOperationBody(bodyObject: JsonObject, tags: Map<String, String>?): String {
         if (!tags.isNullOrEmpty()) {
             mergeTags(bodyObject, tags)
         }
         return bodyObject.toString()
     }
 
     private fun mergeTags(bodyObject: JsonObject, tags: Map<String, String>) {
         val existingTags: JsonElement? = bodyObject.get(TAGS_FIELD)
         when {
             existingTags == null || existingTags.isJsonNull -> {
                 bodyObject.add(TAGS_FIELD, gson.toJsonTree(tags))
             }
 
             existingTags.isJsonObject -> {
                 val tagsObject: JsonObject = existingTags.asJsonObject
                 tags.forEach { (key: String, value: String) ->
                     if (tagsObject.has(key)) {
                         mindboxLogW(
                             "WebView operation body `tags` already contains key `$key`; " +
                                 "keeping client value, skipping in-app value"
                         )
                     } else {
                         tagsObject.addProperty(key, value)
                     }
                 }
             }
 
             else -> {
                 mindboxLogW(
                     "WebView operation body `tags` is not a JSON object; " +
                         "keeping client value, skipping in-app tags"
                 )
             }
         }
     }
 }