Coverage Summary for Class: TrackingFailureExtensionKt (cloud.mindbox.mobile_sdk.inapp.domain.extensions)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| TrackingFailureExtensionKt |
75%
(9/12)
|
61.5%
(32/52)
|
45.1%
(23/51)
|
45.8%
(147/321)
|
| TrackingFailureExtensionKt$executeWithFailureTracking$1 |
0%
(0/1)
|
|
0%
(0/1)
|
0%
(0/1)
|
| TrackingFailureExtensionKt$parseOperationBody$1 |
100%
(1/1)
|
50%
(11/22)
|
100%
(10/10)
|
91.8%
(78/85)
|
| Total |
71.4%
(10/14)
|
58.1%
(43/74)
|
53.2%
(33/62)
|
55.3%
(225/407)
|
package cloud.mindbox.mobile_sdk.inapp.domain.extensions
import cloud.mindbox.mobile_sdk.getErrorResponseBodyData
import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppFailureTracker
import cloud.mindbox.mobile_sdk.inapp.domain.models.TargetingData
import cloud.mindbox.mobile_sdk.models.operation.request.OperationBodyRequest
import cloud.mindbox.mobile_sdk.utils.loggingRunCatching
import com.android.volley.NoConnectionError
import com.android.volley.TimeoutError
import com.android.volley.VolleyError
import com.bumptech.glide.load.HttpException
import com.bumptech.glide.load.engine.GlideException
import com.google.gson.Gson
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import cloud.mindbox.mobile_sdk.logger.mindboxLogE
import cloud.mindbox.mobile_sdk.models.operation.request.FailureReason
import kotlinx.coroutines.TimeoutCancellationException
internal fun VolleyError.isTimeoutError(): Boolean {
return this is TimeoutError || cause is SocketTimeoutException
}
internal fun VolleyError.isNoConnectionError(): Boolean {
return this is NoConnectionError
}
internal fun VolleyError.isServerError(): Boolean {
val statusCode = networkResponse?.statusCode ?: return false
return statusCode in 500..599
}
internal fun Throwable.shouldTrackTargetingError(): Boolean {
val volleyError = cause.asVolleyError() ?: return false
return volleyError.isServerError() && !volleyError.isTimeoutError() && !volleyError.isNoConnectionError()
}
internal fun Throwable.shouldTrackImageDownloadError(): Boolean {
if (cause is TimeoutCancellationException) return false
val glideException = cause as? GlideException ?: return true
return glideException.rootCauses.none { rootCause ->
when {
rootCause is SocketTimeoutException || rootCause.cause is SocketTimeoutException -> true
rootCause is HttpException && rootCause.statusCode <= 0 ->
rootCause.cause is UnknownHostException || rootCause.cause is ConnectException
else -> false
}
}
}
internal fun Throwable?.asVolleyError(): VolleyError? = this as? VolleyError
internal fun Throwable.getVolleyErrorDetails(): String {
val volleyError = this.asVolleyError() ?: return "volleyError = null"
val statusCode = volleyError.networkResponse?.statusCode ?: "timeout error"
val networkTimeMs = volleyError.networkTimeMs
val body = volleyError.getErrorResponseBodyData()
return "statusCode=$statusCode, networkTimeMs=$networkTimeMs, body=$body"
}
internal fun TargetingData.getProductFromTargetingData(): Pair<String, String>? {
if (this !is TargetingData.OperationBody) return null
return parseOperationBody(this.operationBody)
}
private fun parseOperationBody(operationBody: String?): Pair<String, String>? =
loggingRunCatching(null) {
val body = Gson().fromJson(operationBody, OperationBodyRequest::class.java) ?: return@loggingRunCatching null
body.viewProductRequest
?.product
?.ids
?.ids
?.entries
?.firstOrNull()
?.takeIf { entry ->
entry.value?.isNotBlank() == true
}
?.let { entry -> entry.key to entry.value!! }
}
internal fun InAppFailureTracker.sendPresentationFailure(
inAppId: String,
errorDescription: String,
throwable: Throwable? = null
) {
val errorDetails = when {
throwable != null -> "$errorDescription: ${throwable.message ?: "Unknown error"}"
else -> errorDescription
}
mindboxLogE(errorDetails)
sendFailure(
inAppId = inAppId,
failureReason = FailureReason.PRESENTATION_FAILED,
errorDetails = errorDetails
)
}
internal fun InAppFailureTracker.sendFailureWithContext(
inAppId: String,
failureReason: FailureReason,
errorDescription: String,
throwable: Throwable? = null
) {
val errorDetails = when {
throwable != null -> "$errorDescription: ${throwable.message ?: "Unknown error"}"
else -> errorDescription
}
mindboxLogE(errorDetails)
sendFailure(
inAppId = inAppId,
failureReason = failureReason,
errorDetails = errorDetails
)
}
internal inline fun <T> InAppFailureTracker.executeWithFailureTracking(
inAppId: String,
failureReason: FailureReason,
errorDescription: String,
crossinline onFailure: () -> Unit = {},
block: () -> T
): Result<T> {
return runCatching(block).onFailure { throwable ->
sendFailureWithContext(
inAppId = inAppId,
failureReason = failureReason,
errorDescription = errorDescription,
throwable = throwable
)
onFailure()
}
}