From 37bc36ce02dc43d69b6c5b9488391ba67d4fb090 Mon Sep 17 00:00:00 2001 From: Marton Matusek Date: Tue, 16 Apr 2024 16:44:45 +0200 Subject: [PATCH] feat(multi-id): ContactTokenHeaderMapper adds x-contact-token header to all predict requests SUITEDEV-35599 Co-authored-by: megamegax --- .../emarsys/mapper/MerchantIdHeaderMapper.kt | 8 +- .../mapper/ContactTokenHeaderMapperTest.kt | 77 ++++++++++++++----- .../util/RequestModelHelperTest.kt | 34 +++++++- .../mapper/ContactTokenHeaderMapper.kt | 23 ++++-- .../mobileengage/util/RequestModelHelper.kt | 17 +++- 5 files changed, 126 insertions(+), 33 deletions(-) diff --git a/emarsys/src/main/java/com/emarsys/mapper/MerchantIdHeaderMapper.kt b/emarsys/src/main/java/com/emarsys/mapper/MerchantIdHeaderMapper.kt index 84aff005..cdcf5101 100644 --- a/emarsys/src/main/java/com/emarsys/mapper/MerchantIdHeaderMapper.kt +++ b/emarsys/src/main/java/com/emarsys/mapper/MerchantIdHeaderMapper.kt @@ -18,15 +18,15 @@ class MerchantIdHeaderMapper( override fun createHeaders(requestModel: RequestModel): Map { val headers: MutableMap = requestModel.headers.toMutableMap() - if(!predictRequestContext.merchantId.isNullOrBlank()) { + if (!predictRequestContext.merchantId.isNullOrBlank()) { headers[MERCHANT_ID_HEADER] = predictRequestContext.merchantId!! } - return headers } override fun shouldMapRequestModel(requestModel: RequestModel): Boolean { - return requestModelHelper.isMobileEngageSetContactRequest(requestModel) || - requestModelHelper.isRefreshContactTokenRequest(requestModel) + return (requestModelHelper.isMobileEngageSetContactRequest(requestModel) + || requestModelHelper.isRefreshContactTokenRequest(requestModel)) + && !predictRequestContext.merchantId.isNullOrBlank() } } \ No newline at end of file diff --git a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapperTest.kt b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapperTest.kt index 55751088..3da61f8d 100644 --- a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapperTest.kt +++ b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapperTest.kt @@ -18,6 +18,7 @@ import io.kotest.matchers.shouldNotBe import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock +import org.mockito.kotlin.stub class ContactTokenHeaderMapperTest : AnnotationSpec() { private companion object { @@ -27,6 +28,7 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { const val REQUEST_ID = "request_id" const val HARDWARE_ID = "hwid" const val APPLICATION_CODE = "applicationCode" + const val MERCHANT_ID = "merchantId" } private lateinit var contactTokenHeaderMapper: ContactTokenHeaderMapper @@ -40,7 +42,6 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { @Before - @Suppress("UNCHECKED_CAST") fun setUp() { mockContactTokenStorage = mock { on { get() } doReturn CONTACT_TOKEN @@ -72,18 +73,22 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { on { isMobileEngageRequest(any()) } doReturn true on { isMobileEngageSetContactRequest(any()) } doReturn false on { isRefreshContactTokenRequest(any()) } doReturn false + on { isPredictRequest(any()) } doReturn false } - contactTokenHeaderMapper = ContactTokenHeaderMapper(mockRequestContext, mockRequestModelHelper) + contactTokenHeaderMapper = + ContactTokenHeaderMapper(mockRequestContext, mockRequestModelHelper) } @Test fun testMap_shouldAddHeaders_whenRequestIsForMobileEngage() { val originalRequestModels = createMobileEngageRequest() - val expectedRequestModels = createMobileEngageRequest(extraHeaders = mapOf( + val expectedRequestModels = createMobileEngageRequest( + extraHeaders = mapOf( "X-Contact-Token" to CONTACT_TOKEN - )) + ) + ) val result = contactTokenHeaderMapper.map(originalRequestModels) @@ -95,9 +100,11 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { fun testMap_shouldAddHeaders_whenCompositeRequestIsForMobileEngage() { val originalRequestModels = createCustomEventCompositeRequest() - val expectedRequestModels = createCustomEventCompositeRequest(extraHeaders = mapOf( + val expectedRequestModels = createCustomEventCompositeRequest( + extraHeaders = mapOf( "X-Contact-Token" to CONTACT_TOKEN - )) + ) + ) val result = contactTokenHeaderMapper.map(originalRequestModels) @@ -137,7 +144,27 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { result shouldBe originalRequestModels } - private fun createMobileEngageRequest(extraHeaders: Map = mapOf()) = RequestModel( + @Test + fun testMap_shouldAddHeaders_whenRequestIsForPredict() { + val originalRequestModel = createPredictRequest() + val expectedRequestModel = createPredictRequest( + extraHeaders = mapOf( + "X-Contact-Token" to CONTACT_TOKEN + ) + ) + mockRequestModelHelper.stub { + on { isMobileEngageRequest(originalRequestModel) } doReturn false + on { isPredictRequest(originalRequestModel) } doReturn true + } + + val result = contactTokenHeaderMapper.map(originalRequestModel) + + result shouldBe expectedRequestModel + result shouldNotBe originalRequestModel + } + + private fun createMobileEngageRequest(extraHeaders: Map = mapOf()) = + RequestModel( "https://me-client.eservice.emarsys.net/v3/apps/$APPLICATION_CODE/client", RequestMethod.POST, null, @@ -145,9 +172,10 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { TIMESTAMP, Long.MAX_VALUE, REQUEST_ID - ) + ) - private fun createCustomEventCompositeRequest(extraHeaders: Map = mapOf()) = CompositeRequestModel( + private fun createCustomEventCompositeRequest(extraHeaders: Map = mapOf()) = + CompositeRequestModel( "0", "https://me-client.eservice.emarsys.net/v3/apps/$APPLICATION_CODE/client", RequestMethod.POST, @@ -156,9 +184,10 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { TIMESTAMP, Long.MAX_VALUE, arrayOf(REQUEST_ID) - ) + ) - private fun createRefreshContactTokenRequest(extraHeaders: Map = mapOf()) = RequestModel( + private fun createRefreshContactTokenRequest(extraHeaders: Map = mapOf()) = + RequestModel( "https://me-client.eservice.emarsys.net/v3/apps/${APPLICATION_CODE}/client/contact-token", RequestMethod.POST, RequestPayloadUtils.createRefreshContactTokenPayload(mockRequestContext), @@ -166,16 +195,26 @@ class ContactTokenHeaderMapperTest : AnnotationSpec() { TIMESTAMP, Long.MAX_VALUE, REQUEST_ID - ) + ) private fun createNonMobileEngageRequest() = RequestModel( - "https://not-mobile-engage.com", - RequestMethod.POST, - null, - mapOf(), - TIMESTAMP, - Long.MAX_VALUE, - REQUEST_ID + "https://not-mobile-engage.com", + RequestMethod.POST, + null, + mapOf(), + TIMESTAMP, + Long.MAX_VALUE, + REQUEST_ID + ) + + private fun createPredictRequest(extraHeaders: Map = mapOf()) = RequestModel( + "https://recommender.scarabresearch.com/merchants/$MERCHANT_ID", + RequestMethod.POST, + null, + extraHeaders, + TIMESTAMP, + Long.MAX_VALUE, + REQUEST_ID ) } \ No newline at end of file diff --git a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/util/RequestModelHelperTest.kt b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/util/RequestModelHelperTest.kt index 2406ad80..bd562e36 100644 --- a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/util/RequestModelHelperTest.kt +++ b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/util/RequestModelHelperTest.kt @@ -24,11 +24,13 @@ class RequestModelHelperTest : AnnotationSpec() { const val INBOX_HOST = "https://mobile-events.eservice.emarsys.net/v3" const val INBOX_BASE = "$INBOX_HOST/apps/%s/inbox" const val REMOTE_CONFIG_HOST = "https://mobile-sdk-config.gservice.emarsys.net" + const val PREDICT_HOST = "https://recommender.scarabresearch.com/merchants" } private lateinit var mockClientServiceProvider: ServiceEndpointProvider private lateinit var mockEventServiceProvider: ServiceEndpointProvider private lateinit var mockMessageInboxServiceProvider: ServiceEndpointProvider + private lateinit var mockPredictServiceEndpointProvider: ServiceEndpointProvider private lateinit var mockRequestModel: RequestModel private lateinit var requestModelHelper: RequestModelHelper @@ -45,8 +47,16 @@ class RequestModelHelperTest : AnnotationSpec() { mockMessageInboxServiceProvider = mock { on { provideEndpointHost() } doReturn INBOX_HOST } + mockPredictServiceEndpointProvider = mock { + on { provideEndpointHost() } doReturn PREDICT_HOST + } - requestModelHelper = RequestModelHelper(mockClientServiceProvider, mockEventServiceProvider, mockMessageInboxServiceProvider) + requestModelHelper = RequestModelHelper( + mockClientServiceProvider, + mockEventServiceProvider, + mockMessageInboxServiceProvider, + mockPredictServiceEndpointProvider + ) } @@ -202,4 +212,26 @@ class RequestModelHelperTest : AnnotationSpec() { result shouldBe true } + @Test + fun testIsPredictRequest_true_whenItIsPredictRequest() { + val mockRequestModel = mock(RequestModel::class.java).apply { + whenever(url).thenReturn(URL("$PREDICT_HOST/1428C8EE286EC34B")) + } + + val result = requestModelHelper.isPredictRequest(mockRequestModel) + + result shouldBe true + } + + @Test + fun testIsPredictRequest_true_whenItisNotPredictRequest() { + val mockRequestModel = mock(RequestModel::class.java).apply { + whenever(url).thenReturn(URL(INLINE_IN_APP_V3)) + } + + val result = requestModelHelper.isPredictRequest(mockRequestModel) + + result shouldBe false + } + } \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapper.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapper.kt index bfabbcc0..076aaf93 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapper.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/request/mapper/ContactTokenHeaderMapper.kt @@ -4,8 +4,10 @@ import com.emarsys.core.request.model.RequestModel import com.emarsys.mobileengage.MobileEngageRequestContext import com.emarsys.mobileengage.util.RequestModelHelper -class ContactTokenHeaderMapper(override val requestContext: MobileEngageRequestContext, - override val requestModelHelper: RequestModelHelper) : AbstractRequestMapper(requestContext,requestModelHelper) { +class ContactTokenHeaderMapper( + override val requestContext: MobileEngageRequestContext, + override val requestModelHelper: RequestModelHelper +) : AbstractRequestMapper(requestContext, requestModelHelper) { override fun createHeaders(requestModel: RequestModel): Map { val headers: MutableMap = requestModel.headers.toMutableMap() @@ -16,9 +18,18 @@ class ContactTokenHeaderMapper(override val requestContext: MobileEngageRequestC } override fun shouldMapRequestModel(requestModel: RequestModel): Boolean { - return requestModelHelper.isMobileEngageRequest(requestModel) - && !requestModelHelper.isRefreshContactTokenRequest(requestModel) - && !requestModelHelper.isMobileEngageSetContactRequest(requestModel) - && requestContext.contactTokenStorage.get() != null + return (isNotSetContactAndNotRefreshContactTokenMobileEngageRequest(requestModel) + || isPredictRequest(requestModel)) + && isContactTokenAvailable() } + + private fun isNotSetContactAndNotRefreshContactTokenMobileEngageRequest(requestModel: RequestModel) = + (requestModelHelper.isMobileEngageRequest(requestModel) + && !requestModelHelper.isRefreshContactTokenRequest(requestModel) + && !requestModelHelper.isMobileEngageSetContactRequest(requestModel)) + + private fun isPredictRequest(requestModel: RequestModel) = + requestModelHelper.isPredictRequest(requestModel) + + private fun isContactTokenAvailable() = requestContext.contactTokenStorage.get() != null } \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/util/RequestModelHelper.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/util/RequestModelHelper.kt index 6e427038..e2a3c9d1 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/util/RequestModelHelper.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/util/RequestModelHelper.kt @@ -5,9 +5,12 @@ import com.emarsys.core.endpoint.ServiceEndpointProvider import com.emarsys.core.request.model.RequestModel @Mockable -class RequestModelHelper(private val clientServiceEndpointProvider: ServiceEndpointProvider, - private val eventServiceEndpointProvider: ServiceEndpointProvider, - private val messageInboxServiceEndpointProvider: ServiceEndpointProvider) { +class RequestModelHelper( + private val clientServiceEndpointProvider: ServiceEndpointProvider, + private val eventServiceEndpointProvider: ServiceEndpointProvider, + private val messageInboxServiceEndpointProvider: ServiceEndpointProvider, + private val predictServiceEndpointProvider: ServiceEndpointProvider +) { fun isMobileEngageRequest(requestModel: RequestModel): Boolean { @@ -53,7 +56,15 @@ class RequestModelHelper(private val clientServiceEndpointProvider: ServiceEndpo return url.startsWithOneOf(clientServiceUrl) && url.endsWith("client/contact") } + fun isPredictRequest(requestModel: RequestModel): Boolean { + val predictServiceUrl = predictServiceEndpointProvider.provideEndpointHost() + + val url = requestModel.url.toString() + return url.startsWith(predictServiceUrl) + } + private fun String.startsWithOneOf(vararg patterns: String): Boolean { return patterns.any { this.startsWith(it) } } + } \ No newline at end of file