Skip to content

Commit

Permalink
refactor(MC-2362): Add parsing of url params on open-url action (#684)
Browse files Browse the repository at this point in the history
  • Loading branch information
vasct authored Nov 12, 2024
1 parent 7aff990 commit 97a4897
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import com.clevertap.android.sdk.customviews.CloseImageView;
import com.clevertap.android.sdk.inapp.images.FileResourceProvider;
import com.clevertap.android.sdk.utils.UriHelper;

import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.URLDecoder;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -81,31 +83,47 @@ public void triggerAction(
@NonNull CTInAppAction action,
@Nullable String callToAction,
@Nullable Bundle additionalData) {
if (action.getType() == InAppActionType.OPEN_URL) {
//All URL parameters should be tracked as additional data
final Bundle urlActionData = UriHelper.getAllKeyValuePairs(action.getActionUrl(), false);

// callToAction is handled as a parameter
String callToActionUrlParam = urlActionData.getString(Constants.KEY_C2A);
// no need to keep it in the data bundle
urlActionData.remove(Constants.KEY_C2A);

// add all additional params, overriding the url params if there is a collision
if (additionalData != null) {
urlActionData.putAll(additionalData);
}
// Use the merged data for the action
additionalData = urlActionData;
if (callToActionUrlParam != null) {
// check if there is a deeplink within the callToAction param
final String[] parts = callToActionUrlParam.split(Constants.URL_PARAM_DL_SEPARATOR);
if (parts.length == 2) {
// Decode it here as it is not decoded by UriHelper
try {
// Extract the actual callToAction value
callToActionUrlParam = URLDecoder.decode(parts[0], "UTF-8");
} catch (UnsupportedEncodingException | IllegalArgumentException e) {
config.getLogger().debug("Error parsing c2a param", e);
}
// use the url from the callToAction param
action = CTInAppAction.createOpenUrlAction(parts[1]);
}
}
if (callToAction == null) {
// Use the url param value only if no other value is passed
callToAction = callToActionUrlParam;
}
}
Bundle actionData = notifyActionTriggered(action, callToAction != null ? callToAction : "", additionalData);
didDismiss(actionData);
}

void openActionUrl(String url) {
try {
final Bundle formData = UriHelper.getAllKeyValuePairs(url, false);

String callToAction = formData.getString(Constants.KEY_C2A);
if (callToAction != null) {
final String[] parts = callToAction.split(Constants.URL_PARAM_DL_SEPARATOR);
if (parts.length == 2) {
// Decode it here as wzrk_c2a is not decoded by UriHelper
callToAction = URLDecoder.decode(parts[0], "UTF-8");
formData.putString(Constants.KEY_C2A, callToAction);
url = parts[1];
}
}

CTInAppAction action = CTInAppAction.createOpenUrlAction(url);
config.getLogger().debug("Executing call to action for in-app: " + url);
triggerAction(action, callToAction != null ? callToAction : "", formData);
} catch (Throwable t) {
config.getLogger().debug("Error parsing the in-app notification action!", t);
}
triggerAction(CTInAppAction.createOpenUrlAction(url), null, null);
}

public void didDismiss(Bundle data) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package com.clevertap.android.sdk.inapp

import android.net.Uri
import android.os.Bundle
import com.clevertap.android.sdk.Constants
import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class CTInAppBaseFragmentTest {

@Test
fun `triggerAction should parse url parameters as additionalData`() {
val fragment = spyk<CTInAppBaseFragment>()
val inAppListener = mockk<InAppListener>(relaxed = true)
every { fragment.listener } returns inAppListener

val param1 = "value"
val param2 = "value 2"
val param3 = "5"
val url = Uri.parse("https://clevertap.com")
.buildUpon()
.appendQueryParameter("param1", param1)
.appendQueryParameter("param2", param2)
.appendQueryParameter("param3", param3)
.build().toString()

fragment.triggerAction(CTInAppAction.createOpenUrlAction(url), null, null)
verify {
inAppListener.inAppNotificationActionTriggered(
inAppNotification = any(),
action = match { action ->
action.type == InAppActionType.OPEN_URL
&& action.actionUrl == url
},
callToAction = any(),
additionalData = match { data ->
param1 == data.getString("param1")
&& param2 == data.getString("param2")
&& param3 == data.getString("param3")
},
activityContext = any()
)
}
}

@Test
fun `triggerAction should merge url parameters with provided additionalData `() {
val fragment = spyk<CTInAppBaseFragment>()
val inAppListener = mockk<InAppListener>(relaxed = true)
every { fragment.listener } returns inAppListener

val urlParam1 = "value"
val urlParam2 = "value 2"
val urlParam3 = "5"
val url = Uri.parse("https://clevertap.com")
.buildUpon()
.appendQueryParameter("param1", urlParam1)
.appendQueryParameter("param2", urlParam2)
.appendQueryParameter("param3", urlParam3)
.build().toString()

val dataParam1 = "dataValue"
val dataParam2 = "data value 2"
val data = Bundle().apply {
putString("param1", dataParam1)
putString("param2", dataParam2)
}

fragment.triggerAction(CTInAppAction.createOpenUrlAction(url), null, data)
verify {
inAppListener.inAppNotificationActionTriggered(
inAppNotification = any(),
action = any(),
callToAction = any(),
additionalData = match { data ->
dataParam1 == data.getString("param1")
&& dataParam2 == data.getString("param2")
&& urlParam3 == data.getString("param3")
},
activityContext = any()
)
}
}

@Test
fun `triggerAction should use callToAction argument or c2a url param`() {
val fragment = spyk<CTInAppBaseFragment>()
val inAppListener = mockk<InAppListener>(relaxed = true)
every { fragment.listener } returns inAppListener

val callToActionParam = "c2aParam"
val url = Uri.parse("https://clevertap.com")
.buildUpon()
.appendQueryParameter(Constants.KEY_C2A, callToActionParam)
.build().toString()

fragment.triggerAction(CTInAppAction.createOpenUrlAction(url), null, null)
verify {
inAppListener.inAppNotificationActionTriggered(
inAppNotification = any(),
action = any(),
callToAction = callToActionParam,
additionalData = any(),
activityContext = any()
)
}

val callToActionArgument = "argument"
fragment.triggerAction(CTInAppAction.createOpenUrlAction(url), callToActionArgument, null)
verify {
inAppListener.inAppNotificationActionTriggered(
inAppNotification = any(),
action = any(),
callToAction = callToActionArgument,
additionalData = any(),
activityContext = any()
)
}
}

@Test
fun `triggerAction should parse c2a url param with __dl__ data`() {
val fragment = spyk<CTInAppBaseFragment>()
val inAppListener = mockk<InAppListener>(relaxed = true)
every { fragment.listener } returns inAppListener

val dl = "https://deeplink.com?param1=asd&param2=value2"
val callToActionParam = "c2aParam"
val param1 = "value"
val url = Uri.parse("https://clevertap.com")
.buildUpon()
.appendQueryParameter(Constants.KEY_C2A, "${callToActionParam}__dl__$dl")
.appendQueryParameter("param1", param1)
.build().toString()

fragment.triggerAction(CTInAppAction.createOpenUrlAction(url), null, null)
verify {
inAppListener.inAppNotificationActionTriggered(
inAppNotification = any(),
action = any(),
callToAction = callToActionParam,
additionalData = match { data ->
data.size() == 1
&& param1 == data.getString("param1")
},
activityContext = any()
)
}

val callToActionArgument = "argument"
fragment.triggerAction(CTInAppAction.createOpenUrlAction(url), callToActionArgument, null)
verify {
inAppListener.inAppNotificationActionTriggered(
inAppNotification = any(),
action = any(),
callToAction = callToActionArgument,
additionalData = match { data ->
data.size() == 1
&& param1 == data.getString("param1")
},
activityContext = any()
)
}
}
}

0 comments on commit 97a4897

Please sign in to comment.