Coverage Summary for Class: InAppMapper (cloud.mindbox.mobile_sdk.inapp.data.mapper)

Class Class, % Method, % Branch, % Line, % Instruction, %
InAppMapper 100% (1/1) 43.8% (7/16) 22.9% (48/210) 31.8% (95/299) 33% (554/1678)


 package cloud.mindbox.mobile_sdk.inapp.data.mapper
 
 import cloud.mindbox.mobile_sdk.convertToZonedDateTime
 import cloud.mindbox.mobile_sdk.enumValue
 import cloud.mindbox.mobile_sdk.inapp.data.dto.BackgroundDto
 import cloud.mindbox.mobile_sdk.inapp.data.dto.ElementDto
 import cloud.mindbox.mobile_sdk.inapp.data.dto.GeoTargetingDto
 import cloud.mindbox.mobile_sdk.inapp.data.dto.PayloadDto
 import cloud.mindbox.mobile_sdk.inapp.domain.models.*
 import cloud.mindbox.mobile_sdk.inapp.domain.models.ProductResponse
 import cloud.mindbox.mobile_sdk.models.TimeSpan
 import cloud.mindbox.mobile_sdk.models.TreeTargetingDto
 import cloud.mindbox.mobile_sdk.models.operation.Ids
 import cloud.mindbox.mobile_sdk.models.operation.request.IdsRequest
 import cloud.mindbox.mobile_sdk.models.operation.request.SegmentationCheckRequest
 import cloud.mindbox.mobile_sdk.models.operation.request.SegmentationDataRequest
 import cloud.mindbox.mobile_sdk.models.operation.response.*
 import cloud.mindbox.mobile_sdk.models.operation.response.FrequencyDto.FrequencyOnceDto.Companion.FREQUENCY_KIND_LIFETIME
 import cloud.mindbox.mobile_sdk.models.operation.response.FrequencyDto.FrequencyOnceDto.Companion.FREQUENCY_KIND_SESSION
 import cloud.mindbox.mobile_sdk.models.toMilliseconds
 import cloud.mindbox.mobile_sdk.monitoring.domain.models.LogRequest
 import kotlin.math.roundToInt
 
 internal class InAppMapper {
     fun mapToProductSegmentationResponse(productSegmentationResponseDto: ProductSegmentationResponseDto): ProductSegmentationResponseWrapper {
         return ProductSegmentationResponseWrapper(
             productSegmentationResponseDto.products?.map { productResponseDto ->
                 ProductResponse(
                     productList = productResponseDto?.segmentations?.map { productSegmentations ->
                         ProductSegmentationResponse(
                             segmentationExternalId = productSegmentations?.ids?.ids?.values?.first()
                                 ?: "",
                             segmentExternalId = productSegmentations?.segment?.ids?.ids?.values?.first()
                                 ?: ""
                         )
                     } ?: emptyList()
                 )
             } ?: emptyList()
         )
     }
 
     fun mapGeoTargetingDtoToGeoTargeting(geoTargetingDto: GeoTargetingDto): GeoTargeting {
         return GeoTargeting(
             geoTargetingDto.cityId ?: "",
             geoTargetingDto.regionId ?: "",
             geoTargetingDto.countryId ?: ""
         )
     }
 
     fun mapToInAppDto(
         inAppDtoBlank: InAppConfigResponseBlank.InAppDtoBlank,
         delayTime: TimeSpan?,
         formDto: FormDto?,
         frequencyDto: FrequencyDto,
         targetingDto: TreeTargetingDto?,
     ): InAppDto {
         return inAppDtoBlank.let { inApp ->
             InAppDto(
                 id = inApp.id,
                 isPriority = inApp.isPriority,
                 delayTime = delayTime,
                 sdkVersion = inApp.sdkVersion,
                 targeting = targetingDto,
                 frequency = frequencyDto,
                 form = formDto,
                 tags = inApp.tags,
             )
         }
     }
 
     fun mapToLogRequestDto(
         logRequestDtoBlank: LogRequestDtoBlank,
     ): LogRequestDto {
         return LogRequestDto(
             requestId = logRequestDtoBlank.requestId,
             deviceId = logRequestDtoBlank.deviceId,
             from = logRequestDtoBlank.from,
             to = logRequestDtoBlank.to
         )
     }
 
     private fun mapBackgroundLayers(layers: List<BackgroundDto.LayerDto?>?): List<Layer> {
         return layers?.map { layerDto ->
             when (layerDto) {
                 is BackgroundDto.LayerDto.ImageLayerDto -> {
                     Layer.ImageLayer(
                         action = when (layerDto.action) {
                             is BackgroundDto.LayerDto.ImageLayerDto.ActionDto.RedirectUrlActionDto -> {
                                 Layer.ImageLayer.Action.RedirectUrlAction(
                                     url = layerDto.action.value!!,
                                     payload = layerDto.action.intentPayload!!
                                 )
                             }
 
                             is BackgroundDto.LayerDto.ImageLayerDto.ActionDto.PushPermissionActionDto -> {
                                 Layer.ImageLayer.Action.PushPermissionAction(
                                     payload = layerDto.action.intentPayload!!
                                 )
                             }
 
                             else -> {
                                 error("Unknown action cannot be mapped. Should never happen because of validators")
                             }
                         },
                         source = when (layerDto.source) {
                             is BackgroundDto.LayerDto.ImageLayerDto.SourceDto.UrlSourceDto -> {
                                 Layer.ImageLayer.Source.UrlSource(
                                     url = layerDto.source.value!!
                                 )
                             }
 
                             else -> {
                                 error("Unknown source cannot be mapped. Should never happen because of validators")
                             }
                         }
                     )
                 }
                 is BackgroundDto.LayerDto.WebViewLayerDto -> {
                     Layer.WebViewLayer(
                         baseUrl = layerDto.baseUrl,
                         contentUrl = layerDto.contentUrl,
                         params = layerDto.params ?: emptyMap(),
                         type = layerDto.type
                     )
                 }
 
                 else -> {
                     error("Unknown layer cannot be mapped. Should never happen because of validators")
                 }
             }
         }!!
     }
 
     private fun mapElements(elements: List<ElementDto?>?): List<Element> {
         return elements?.map { elementDto ->
             when (elementDto) {
                 is ElementDto.CloseButtonElementDto -> {
                     Element.CloseButton(
                         color = elementDto.color!!,
                         lineWidth = elementDto.lineWidth.toString()
                             .toDouble(),
                         size = Element.CloseButton.Size(
                             width = elementDto.size?.width!!,
                             height = elementDto.size.height!!,
                             kind = when (elementDto.size.kind) {
                                 "dp" -> {
                                     Element.CloseButton.Size.Kind.DP
                                 }
 
                                 else -> {
                                     error("Unknown size cannot be mapped. Should never happen because of validators")
                                 }
                             }
                         ),
                         position = Element.CloseButton.Position(
                             top = elementDto.position?.margin?.top!!,
                             right = elementDto.position.margin.right!!,
                             left = elementDto.position.margin.left!!,
                             bottom = elementDto.position.margin.bottom!!,
                             kind = when (elementDto.position.margin.kind) {
                                 "proportion" -> {
                                     Element.CloseButton.Position.Kind.PROPORTION
                                 }
 
                                 else -> {
                                     error("Unknown margin cannot be mapped. Should never happen because of validators")
                                 }
                             }
                         )
                     )
                 }
 
                 else -> {
                     error("Unknown element cannot be mapped. Should never happen because of validators")
                 }
             }
         } ?: emptyList()
     }
 
     private fun getDelay(item: FrequencyDto): Frequency.Delay {
         return when (item) {
             is FrequencyDto.FrequencyOnceDto -> {
                 when {
                     item.kind.equals(
                         other = FREQUENCY_KIND_LIFETIME,
                         ignoreCase = true
                     ) -> Frequency.Delay.LifetimeDelay
 
                     item.kind.equals(
                         other = FREQUENCY_KIND_SESSION,
                         ignoreCase = true
                     ) -> Frequency.Delay.OneTimePerSession
 
                     else -> error("Unknown kind cannot be mapped. Should never happen because of validators")
                 }
             }
 
             is FrequencyDto.FrequencyPeriodicDto -> {
                 when {
                     item.unit.equals(
                         other = FrequencyDto.FrequencyPeriodicDto.FREQUENCY_UNIT_SECONDS,
                         ignoreCase = true
                     ) -> {
                         Frequency.Delay.TimeDelay(item.value, InAppTime.SECONDS)
                     }
 
                     item.unit.equals(
                         other = FrequencyDto.FrequencyPeriodicDto.FREQUENCY_UNIT_HOURS,
                         ignoreCase = true
                     ) -> {
                         Frequency.Delay.TimeDelay(item.value, InAppTime.HOURS)
                     }
 
                     item.unit.equals(
                         other = FrequencyDto.FrequencyPeriodicDto.FREQUENCY_UNIT_DAYS,
                         ignoreCase = true
                     ) -> {
                         Frequency.Delay.TimeDelay(item.value, InAppTime.DAYS)
                     }
 
                     item.unit.equals(
                         other = FrequencyDto.FrequencyPeriodicDto.FREQUENCY_UNIT_MINUTES,
                         ignoreCase = true
                     ) -> {
                         Frequency.Delay.TimeDelay(item.value, InAppTime.MINUTES)
                     }
 
                     else -> error("Unknown time unit cannot be mapped. Should never happen because of validators")
                 }
             }
         }
     }
 
     fun mapToInAppConfig(
         inAppConfigResponse: InAppConfigResponse?,
     ): InAppConfig {
         return inAppConfigResponse?.let {
             InAppConfig(
                 inApps = inAppConfigResponse.inApps?.map { inAppDto ->
                     InApp(
                         id = inAppDto.id,
                         isPriority = inAppDto.isPriority,
                         delayTime = inAppDto.delayTime.toMilliseconds(),
                         targeting = mapNodesDtoToNodes(listOf(inAppDto.targeting!!)).first(),
                         form = Form(
                             variants = inAppDto.form?.variants?.map { payloadDto ->
                                 when (payloadDto) {
                                     is PayloadDto.ModalWindowDto -> {
                                         val layers = mapBackgroundLayers(payloadDto.content?.background?.layers)
                                         when (layers.firstOrNull()) {
                                             is Layer.WebViewLayer -> InAppType.WebView(
                                                 inAppId = inAppDto.id,
                                                 type = BackgroundDto.LayerDto.WebViewLayerDto.WEBVIEW_TYPE_JSON_NAME,
                                                 layers = layers,
                                             )
                                             else -> InAppType.ModalWindow(
                                                 type = PayloadDto.ModalWindowDto.MODAL_JSON_NAME,
                                                 layers = layers,
                                                 inAppId = inAppDto.id,
                                                 elements = mapElements(payloadDto.content?.elements)
                                             )
                                         }
                                     }
 
                                     is PayloadDto.SnackbarDto -> {
                                         InAppType.Snackbar(
                                             inAppId = inAppDto.id,
                                             type = PayloadDto.SnackbarDto.SNACKBAR_JSON_NAME,
                                             layers = mapBackgroundLayers(payloadDto.content?.background?.layers),
                                             elements = mapElements(payloadDto.content?.elements),
                                             position = InAppType.Snackbar.Position(
                                                 gravity = InAppType.Snackbar.Position.Gravity(
                                                     horizontal = InAppType.Snackbar.Position.Gravity.HorizontalGravity.CENTER,
                                                     vertical = if (payloadDto.content?.position?.gravity?.vertical!! == "top") InAppType.Snackbar.Position.Gravity.VerticalGravity.TOP else InAppType.Snackbar.Position.Gravity.VerticalGravity.BOTTOM
                                                 ),
                                                 margin = InAppType.Snackbar.Position.Margin(
                                                     kind = when (payloadDto.content.position.margin.kind) {
                                                         "dp" -> {
                                                             InAppType.Snackbar.Position.Margin.MarginKind.DP
                                                         }
 
                                                         else -> {
                                                             error("Unknown margin cannot be mapped. Should never happen because of validators")
                                                         }
                                                     },
                                                     top = payloadDto.content.position.margin.top!!.roundToInt(),
                                                     left = payloadDto.content.position.margin.left!!.roundToInt(),
                                                     right = payloadDto.content.position.margin.right!!.roundToInt(),
                                                     bottom = payloadDto.content.position.margin.bottom!!.roundToInt()
                                                 ),
                                             )
                                         )
                                     }
                                     null -> {
                                         return InAppConfig(
                                             listOf(),
                                             listOf(),
                                             mapOf(),
                                             listOf()
                                         ) // should never trigger because of validator
                                     }
                                 }
                             } ?: emptyList()
                         ),
                         minVersion = inAppDto.sdkVersion?.minVersion,
                         maxVersion = inAppDto.sdkVersion?.maxVersion,
                         frequency = Frequency(getDelay(inAppDto.frequency)),
                         tags = inAppDto.tags?.takeIf { it.isNotEmpty() }
                     )
                 } ?: emptyList(),
                 monitoring = inAppConfigResponse.monitoring?.map {
                     LogRequest(
                         requestId = it.requestId,
                         deviceId = it.deviceId,
                         from = it.from.convertToZonedDateTime(),
                         to = it.to.convertToZonedDateTime()
                     )
                 } ?: emptyList(),
                 operations = inAppConfigResponse.settings?.operations?.map { (key, value) ->
                     key.enumValue<OperationName>() to OperationSystemName(value.systemName.lowercase())
                 }?.toMap() ?: emptyMap(),
                 abtests = inAppConfigResponse.abtests?.map { dto ->
                     ABTest(
                         id = dto.id,
                         minVersion = dto.sdkVersion?.minVersion,
                         maxVersion = dto.sdkVersion?.maxVersion,
                         salt = dto.salt,
                         variants = dto.variants?.map { variantDto ->
                             ABTest.Variant(
                                 id = variantDto.id,
                                 type = variantDto.objects!!.first().type!!,
                                 kind = variantDto.objects.first().kind.enumValue(),
                                 inapps = variantDto.objects.first().inapps ?: listOf(),
                                 lower = variantDto.modulus!!.lower!!,
                                 upper = variantDto.modulus.upper!!,
                             )
                         } ?: listOf()
                     )
                 } ?: listOf()
             )
         } ?: InAppConfig(listOf(), listOf(), mapOf(), listOf())
     }
 
     /**
      * Cast is ok as long as validator removes all the in-apps with null values
      * **/
 
     @Suppress("UNCHECKED_CAST")
     private fun mapNodesDtoToNodes(
         nodesDto: List<TreeTargetingDto>,
     ): List<TreeTargeting> {
         return nodesDto.map { treeTargetingDto ->
             when (treeTargetingDto) {
                 is TreeTargetingDto.OperationNodeDto -> {
                     OperationNode(
                         TreeTargetingDto.OperationNodeDto.API_METHOD_CALL_JSON_NAME,
                         treeTargetingDto.systemName!!.lowercase()
                     )
                 }
 
                 is TreeTargetingDto.VisitNodeDto -> TreeTargeting.VisitNode(
                     TreeTargetingDto.VisitNodeDto.VISIT_JSON_NAME,
                     treeTargetingDto.kind.enumValue(),
                     treeTargetingDto.value!!
                 )
 
                 is TreeTargetingDto.TrueNodeDto -> TreeTargeting.TrueNode(TreeTargetingDto.TrueNodeDto.TRUE_JSON_NAME)
                 is TreeTargetingDto.IntersectionNodeDto -> TreeTargeting.IntersectionNode(
                     type = TreeTargetingDto.IntersectionNodeDto.AND_JSON_NAME,
                     nodes = mapNodesDtoToNodes(treeTargetingDto.nodes as List<TreeTargetingDto>)
                 )
 
                 is TreeTargetingDto.SegmentNodeDto -> TreeTargeting.SegmentNode(
                     type = TreeTargetingDto.SegmentNodeDto.SEGMENT_JSON_NAME,
                     kind = if (treeTargetingDto.kind == "positive") Kind.POSITIVE else Kind.NEGATIVE,
                     segmentationExternalId = treeTargetingDto.segmentationExternalId!!,
                     segmentExternalId = treeTargetingDto.segmentExternalId!!
                 )
 
                 is TreeTargetingDto.UnionNodeDto -> TreeTargeting.UnionNode(
                     type = TreeTargetingDto.UnionNodeDto.OR_JSON_NAME,
                     nodes = mapNodesDtoToNodes(treeTargetingDto.nodes as List<TreeTargetingDto>)
                 )
 
                 is TreeTargetingDto.CityNodeDto -> TreeTargeting.CityNode(
                     type = TreeTargetingDto.CityNodeDto.CITY_JSON_NAME,
                     kind = if (treeTargetingDto.kind == "positive") Kind.POSITIVE else Kind.NEGATIVE,
                     ids = treeTargetingDto.ids!!.map { it.toString() }
                 )
 
                 is TreeTargetingDto.CountryNodeDto -> TreeTargeting.CountryNode(
                     type = TreeTargetingDto.CountryNodeDto.COUNTRY_JSON_NAME,
                     kind = if (treeTargetingDto.kind == "positive") Kind.POSITIVE else Kind.NEGATIVE,
                     ids = treeTargetingDto.ids!!.map { it.toString() }
                 )
 
                 is TreeTargetingDto.RegionNodeDto -> TreeTargeting.RegionNode(
                     type = TreeTargetingDto.RegionNodeDto.REGION_JSON_NAME,
                     kind = if (treeTargetingDto.kind == "positive") Kind.POSITIVE else Kind.NEGATIVE,
                     ids = treeTargetingDto.ids!!.map { it.toString() }
                 )
 
                 is TreeTargetingDto.ViewProductCategoryNodeDto -> ViewProductCategoryNode(
                     type = TreeTargetingDto.ViewProductCategoryNodeDto.VIEW_PRODUCT_CATEGORY_ID_JSON_NAME,
                     kind = treeTargetingDto.kind.enumValue(),
                     value = treeTargetingDto.value!!
                 )
 
                 is TreeTargetingDto.ViewProductCategoryInNodeDto -> ViewProductCategoryInNode(
                     type = TreeTargetingDto.ViewProductCategoryNodeDto.VIEW_PRODUCT_CATEGORY_ID_JSON_NAME,
                     kind = treeTargetingDto.kind.enumValue(),
                     values = treeTargetingDto.values?.map { dto ->
                         ViewProductCategoryInNode.Value(
                             id = dto.id!!,
                             externalId = dto.externalId!!,
                             externalSystemName = dto.externalSystemName!!
                         )
                     } ?: listOf()
                 )
 
                 is TreeTargetingDto.ViewProductNodeDto -> ViewProductNode(
                     type = TreeTargetingDto.ViewProductNodeDto.VIEW_PRODUCT_ID_JSON_NAME,
                     kind = treeTargetingDto.kind.enumValue(),
                     value = treeTargetingDto.value!!
                 )
 
                 is TreeTargetingDto.ViewProductSegmentNodeDto -> ViewProductSegmentNode(
                     type = TreeTargetingDto.ViewProductSegmentNodeDto.VIEW_PRODUCT_SEGMENT_JSON_NAME,
                     kind = treeTargetingDto.kind.enumValue(),
                     segmentationExternalId = treeTargetingDto.segmentationExternalId!!,
                     segmentExternalId = treeTargetingDto.segmentExternalId!!
                 )
 
                 is TreeTargetingDto.PushPermissionDto -> TreeTargeting.PushPermissionNode(
                     type = TreeTargetingDto.PushPermissionDto.PUSH_PERMISSION_JSON_NAME,
                     value = treeTargetingDto.value!!
                 )
             }
         }
     }
 
     fun mapToSegmentationCheck(segmentationCheckResponse: SegmentationCheckResponse): SegmentationCheckWrapper {
         return SegmentationCheckWrapper(
             status = segmentationCheckResponse.status ?: "",
             customerSegmentations = segmentationCheckResponse.customerSegmentations?.filter { customerSegmentationInAppResponse ->
                 customerSegmentationInAppResponse.segmentation?.ids?.externalId != null
             }?.map { customerSegmentationInAppResponse ->
                 CustomerSegmentationInApp(
                     customerSegmentationInAppResponse.segmentation?.ids?.externalId!!,
                     customerSegmentationInAppResponse.segment?.ids?.externalId ?: ""
                 )
             } ?: emptyList()
         )
     }
 
     fun mapToCustomerSegmentationCheckRequest(inApps: List<InApp>): SegmentationCheckRequest {
         return SegmentationCheckRequest(
             inApps.flatMap { inAppDto ->
                 getTargetingCustomerSegmentationsList(inAppDto.targeting).map { segment ->
                     SegmentationDataRequest(IdsRequest(segment))
                 }
             }.distinctBy {
                 it.ids?.externalId
             })
     }
 
     fun mapToProductSegmentationCheckRequest(
         product: Pair<String, String>,
         inApps: List<InApp>,
     ): ProductSegmentationRequestDto {
         return ProductSegmentationRequestDto(
             products = listOf(
                 ProductRequestDto(
                     Ids(product)
                 )
             ),
             segmentations = inApps.flatMap { inApp ->
                 getTargetingProductSegmentationsList(inApp.targeting).map { segmentation ->
                     SegmentationRequestDto(
                         SegmentationRequestIds(segmentation)
                     )
                 }
             }.distinctBy {
                 it.ids.externalId
             }
         )
     }
 
     fun mapToTtlDto(inAppTtlDtoBlank: SettingsDtoBlank.TtlDtoBlank) = TtlDto(
         inApps = inAppTtlDtoBlank.inApps
     )
 
     private fun getTargetingProductSegmentationsList(targeting: TreeTargeting): List<String> {
         return when (targeting) {
             is TreeTargeting.IntersectionNode -> {
                 targeting.nodes.flatMap { treeTargeting ->
                     getTargetingProductSegmentationsList(treeTargeting)
                 }
             }
 
             is ViewProductSegmentNode -> {
                 listOf(targeting.segmentationExternalId)
             }
 
             is TreeTargeting.UnionNode -> {
                 targeting.nodes.flatMap { treeTargeting ->
                     getTargetingProductSegmentationsList(treeTargeting)
                 }
             }
 
             else -> {
                 emptyList()
             }
         }
     }
 
     private fun getTargetingCustomerSegmentationsList(targeting: TreeTargeting): List<String> {
         return when (targeting) {
             is TreeTargeting.IntersectionNode -> {
                 targeting.nodes.flatMap { treeTargeting ->
                     getTargetingCustomerSegmentationsList(treeTargeting)
                 }
             }
 
             is TreeTargeting.SegmentNode -> {
                 listOf(targeting.segmentationExternalId)
             }
 
             is TreeTargeting.UnionNode -> {
                 targeting.nodes.flatMap { treeTargeting ->
                     getTargetingCustomerSegmentationsList(treeTargeting)
                 }
             }
 
             else -> {
                 emptyList()
             }
         }
     }
 }