diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/Extensions.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/Extensions.kt index e338eaad0..ca5253fd4 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/Extensions.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/Extensions.kt @@ -18,11 +18,14 @@ import androidx.annotation.IdRes import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppType import cloud.mindbox.mobile_sdk.logger.MindboxLoggerImpl import cloud.mindbox.mobile_sdk.utils.LoggingExceptionHandler +import com.android.volley.VolleyError +import com.android.volley.toolbox.HttpHeaderParser import org.threeten.bp.Instant import org.threeten.bp.LocalDateTime import org.threeten.bp.ZoneOffset import org.threeten.bp.ZonedDateTime import org.threeten.bp.format.DateTimeFormatter +import java.nio.charset.Charset import java.util.Queue import kotlin.math.roundToInt @@ -182,4 +185,17 @@ internal fun PackageManager.getPackageInfoCompat(context: Context, flags: Int): } else { getPackageInfo(context.packageName, flags) } +} + +internal fun VolleyError.getErrorResponseBodyData(): String { + return this.networkResponse?.data + ?.takeIf { it.isNotEmpty() } + ?.toString( + Charset.forName( + HttpHeaderParser.parseCharset( + this.networkResponse?.headers ?: emptyMap() + ) + ) + ) + ?: "" } \ No newline at end of file diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppChoosingManagerImpl.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppChoosingManagerImpl.kt index fc3b973f3..e049269cd 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppChoosingManagerImpl.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppChoosingManagerImpl.kt @@ -1,5 +1,6 @@ package cloud.mindbox.mobile_sdk.inapp.domain +import cloud.mindbox.mobile_sdk.getErrorResponseBodyData import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.InAppContentFetcher import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppChoosingManager import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.InAppGeoRepository @@ -7,16 +8,22 @@ import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.InAppSegmen import cloud.mindbox.mobile_sdk.inapp.domain.models.* import cloud.mindbox.mobile_sdk.logger.MindboxLoggerImpl import cloud.mindbox.mobile_sdk.logger.mindboxLogD +import cloud.mindbox.mobile_sdk.logger.mindboxLogI +import cloud.mindbox.mobile_sdk.logger.mindboxLogW import cloud.mindbox.mobile_sdk.models.InAppEventType +import com.android.volley.VolleyError import kotlinx.coroutines.* internal class InAppChoosingManagerImpl( private val inAppGeoRepository: InAppGeoRepository, private val inAppSegmentationRepository: InAppSegmentationRepository, private val inAppContentFetcher: InAppContentFetcher -) : - InAppChoosingManager { +) : InAppChoosingManager { + companion object { + private const val RESPONSE_STATUS_CUSTOMER_SEGMENTS_REQUIRE_CUSTOMER = + "CheckCustomerSegments requires customer" + } override suspend fun chooseInAppToShow( inApps: List, triggerEvent: InAppEventType, @@ -66,11 +73,7 @@ internal class InAppChoosingManagerImpl( inAppSegmentationRepository.setCustomerSegmentationStatus( CustomerSegmentationFetchStatus.SEGMENTATION_FETCH_ERROR ) - MindboxLoggerImpl.e( - this, - "Error fetching customer segmentations", - throwable - ) + handleCustomerSegmentationErrorLog(throwable) } else -> { @@ -124,6 +127,19 @@ internal class InAppChoosingManagerImpl( ) } + private fun handleCustomerSegmentationErrorLog(error: CustomerSegmentationError) { + val volleyError = error.cause as? VolleyError + volleyError?.let { error -> + if ((error.networkResponse?.statusCode == 400) && (error.getErrorResponseBodyData() + .contains(RESPONSE_STATUS_CUSTOMER_SEGMENTS_REQUIRE_CUSTOMER)) + ) { + mindboxLogI("Cannot check customer segment. It's a new customer") + return + } + } + mindboxLogW("Error fetching customer segmentations", error) + } + private class TargetingDataWrapper( override val triggerEventName: String, override val operationBody: String? = null, diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/Requests.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/Requests.kt index e9f5e539c..80ccdbe60 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/Requests.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/Requests.kt @@ -2,7 +2,9 @@ package cloud.mindbox.mobile_sdk.models import android.os.Build import cloud.mindbox.mobile_sdk.BuildConfig -import cloud.mindbox.mobile_sdk.logger.MindboxLoggerImpl +import cloud.mindbox.mobile_sdk.getErrorResponseBodyData +import cloud.mindbox.mobile_sdk.logger.mindboxLogI +import cloud.mindbox.mobile_sdk.logger.mindboxLogW import cloud.mindbox.mobile_sdk.utils.LoggingExceptionHandler import com.android.volley.NetworkResponse import com.android.volley.ParseError @@ -108,25 +110,16 @@ internal data class MindboxRequest( //Logging error responses override fun parseNetworkError(volleyError: VolleyError): VolleyError { LoggingExceptionHandler.runCatching { - MindboxLoggerImpl.e( - this, - "<--- Error ${volleyError.networkResponse?.statusCode} $fullUrl TimeMls:${volleyError.networkTimeMs}; ", - ) try { - volleyError.networkResponse?.allHeaders?.joinToString ( + val json = volleyError.getErrorResponseBodyData() + + logResponseResult(volleyError, json) + + volleyError.networkResponse?.allHeaders?.joinToString( separator = System.getProperty("line.separator") ?: "\n" ) { header -> "${header.name}: ${header.value}" - }?.let { MindboxLoggerImpl.d(this, it) } - - val json = String( - volleyError.networkResponse?.data ?: ByteArray(0), - Charset.forName( - HttpHeaderParser.parseCharset( - volleyError.networkResponse?.headers ?: emptyMap() - ) - ) - ) + }?.let { mindboxLogI(it) } logBodyResponse(json) } catch (e: Exception) { @@ -142,32 +135,47 @@ internal data class MindboxRequest( private fun logResponse(response: NetworkResponse?) { LoggingExceptionHandler.runCatching { - MindboxLoggerImpl.d(this, "<--- ${response?.statusCode} $fullUrl") + mindboxLogI("<--- ${response?.statusCode} $fullUrl") - response?.allHeaders?.joinToString ( + response?.allHeaders?.joinToString( separator = System.getProperty("line.separator") ?: "\n" ) { header -> "${header.name}: ${header.value}" - }?.let { MindboxLoggerImpl.d(this, it) } + }?.let { mindboxLogI(it) } } } private fun logBodyResponse(json: String?) { LoggingExceptionHandler.runCatching { - MindboxLoggerImpl.d(this, "$json") + mindboxLogI("$json") } } private fun logError(e: Exception) { LoggingExceptionHandler.runCatching { - MindboxLoggerImpl.d(this, e.message ?: "Empty message") - MindboxLoggerImpl.d(this, e.stackTraceToString()) + mindboxLogW(e.message ?: "Empty message") + mindboxLogW(e.stackTraceToString()) } } private fun logEndResponse() { LoggingExceptionHandler.runCatching { - MindboxLoggerImpl.d(this, "<--- End of response") + mindboxLogI("<--- End of response") + } + } + + private fun logResponseResult(volleyError: VolleyError?, json: String?) { + LoggingExceptionHandler.runCatching { + val logMessage = buildString { + append("<--- ") + append(if (json?.contains("\"status\": \"Success\"") == true) "Success" else "Error") + append(" ${volleyError?.networkResponse?.statusCode} $fullUrl TimeMls:${volleyError?.networkTimeMs}; ") + } + if (json?.contains("{\"status\": \"Success\"}") == true) { + mindboxLogI(logMessage) + } else { + mindboxLogW(logMessage) + } } } diff --git a/sdk/src/test/java/cloud/mindbox/mobile_sdk/ExtensionsTest.kt b/sdk/src/test/java/cloud/mindbox/mobile_sdk/ExtensionsTest.kt index 2fda90cda..cdd70b4a9 100644 --- a/sdk/src/test/java/cloud/mindbox/mobile_sdk/ExtensionsTest.kt +++ b/sdk/src/test/java/cloud/mindbox/mobile_sdk/ExtensionsTest.kt @@ -2,10 +2,10 @@ package cloud.mindbox.mobile_sdk import android.content.Context import androidx.test.core.app.ApplicationProvider +import com.android.volley.NetworkResponse +import com.android.volley.VolleyError import com.jakewharton.threetenabp.AndroidThreeTen -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkObject +import io.mockk.* import org.junit.Assert.* import org.junit.Before import org.junit.Test @@ -30,7 +30,8 @@ internal class ExtensionsTest { @Test fun `converting zoned date time to string`() { val time: ZonedDateTime = ZonedDateTime.now() - val expectedResult = time.withZoneSameInstant(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")) + val expectedResult = time.withZoneSameInstant(ZoneId.systemDefault()) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")) val actualResult = time.convertToString() assertEquals(expectedResult, actualResult) } @@ -125,4 +126,24 @@ internal class ExtensionsTest { assertTrue(linkedList.contains("tE5t4")) assertEquals(3, linkedList.size) } + + @Test + fun `should return empty string if error has no network response data`() { + val error = VolleyError() + assertEquals("", error.getErrorResponseBodyData()) + } + + @Test + fun `should return response body data`() { + val responseBodyData = "test string" + val networkResponse = NetworkResponse( + 400, + responseBodyData.toByteArray(), + true, + 200, + emptyList() + ) + val error = VolleyError(networkResponse) + assertEquals(responseBodyData, error.getErrorResponseBodyData()) + } } \ No newline at end of file