diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt index 668f8d722..61e8bd59b 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt @@ -1,11 +1,14 @@ package com.mparticle.networking +import android.net.Uri +import com.mparticle.BuildConfig import com.mparticle.MParticle import com.mparticle.MParticleOptions import com.mparticle.internal.AccessUtils +import com.mparticle.internal.ConfigManager import com.mparticle.testutils.BaseCleanInstallEachTest -import junit.framework.TestCase import org.junit.Assert +import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import java.net.MalformedURLException @@ -20,11 +23,170 @@ class MParticleBaseClientImplTest : BaseCleanInstallEachTest() { startMParticle(MParticleOptions.builder(mContext).credentials(apiKey, "secret")) val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { - defaultUrls[endpoint] = baseClientImpl.getUrl(endpoint, endpoint.name) + defaultUrls[endpoint] = baseClientImpl.getUrl(endpoint, endpoint.name, false) } MParticle.setInstance(null) } + @Test + fun testUrlPrefixWithPodRedirection() { + val prefix = "eu1" + val identityUrl = mRandomUtils.getAlphaString(20) + val configUrl = mRandomUtils.getAlphaString(20) + val audienceUrl = mRandomUtils.getAlphaString(20) + val eventsUrl = mRandomUtils.getAlphaString(20) + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions.builder() + .addDomainMapping( + DomainMapping.audienceMapping(audienceUrl) + .build() + ) + .addDomainMapping( + DomainMapping.configMapping(configUrl) + .build() + ) + .addDomainMapping( + DomainMapping.identityMapping(identityUrl) + .build() + ) + .addDomainMapping(DomainMapping.eventsMapping(eventsUrl).build()) + .build() + ) + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val url = + baseClientImpl.getPodUrl(NetworkOptionsManager.MP_URL_PREFIX, prefix, true) + assertEquals("${NetworkOptionsManager.MP_URL_PREFIX}.$prefix.mparticle.com", url) + } + + @Test + fun testUrlPrefixWithoutPodRedirection() { + val prefix = "eu1" + val identityUrl = mRandomUtils.getAlphaString(20) + val configUrl = mRandomUtils.getAlphaString(20) + val audienceUrl = mRandomUtils.getAlphaString(20) + val eventsUrl = mRandomUtils.getAlphaString(20) + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions.builder() + .addDomainMapping( + DomainMapping.audienceMapping(audienceUrl) + .build() + ) + .addDomainMapping( + DomainMapping.configMapping(configUrl) + .build() + ) + .addDomainMapping( + DomainMapping.identityMapping(identityUrl) + .build() + ) + .addDomainMapping(DomainMapping.eventsMapping(eventsUrl).build()) + .build() + ) + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val url = + baseClientImpl.getPodUrl(NetworkOptionsManager.MP_URL_PREFIX, prefix, false) + assertEquals("${NetworkOptionsManager.MP_URL_PREFIX}.mparticle.com", url) + } + + @Test + fun testAllPrefixes() { + val mConfigManager = ConfigManager( + mContext, + MParticle.Environment.Production, + "some api key", + "some api secret", + null, + null, + null, + null, + null, + null + ) + + // Following are the fake APIs for testing purposes. + val map = mapOf( + Pair("us1-1vc4gbp24cdtx6e31s58icnymzy83f1uf", "us1"), + Pair("us2-v2p8lr3w2g90vtpaumbq21zy05cl50qm3", "us2"), + Pair("eu1-bkabfno0b8zpv5bwi2zm2mfa1kfml19al", "eu1"), + Pair("au1-iermuj83dbeoshm0n32f10feotclq6i4a", "au1"), + Pair("st1-k77ivhkbbqf4ce0s3y12zpcthyn1ixfyu", "st1"), + Pair("us3-w1y2y8yj8q58d5bx9u2dvtxzl4cpa7cuf", "us3"), + Pair("kajsdhasdiuyaiudiashhadjhdasjk", "us1") + ) + val identityUrl = mRandomUtils.getAlphaString(20) + val configUrl = mRandomUtils.getAlphaString(20) + val audienceUrl = mRandomUtils.getAlphaString(20) + val eventsUrl = mRandomUtils.getAlphaString(20) + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions.builder() + .addDomainMapping( + DomainMapping.audienceMapping(audienceUrl) + .build() + ) + .addDomainMapping( + DomainMapping.configMapping(configUrl) + .build() + ) + .addDomainMapping( + DomainMapping.identityMapping(identityUrl) + .build() + ) + .addDomainMapping(DomainMapping.eventsMapping(eventsUrl).build()) + .build() + ) + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + map.forEach { key, value -> + mConfigManager?.setCredentials(key, value) + val prefix = mConfigManager?.podPrefix + assertEquals(value, prefix) + assertEquals( + "${NetworkOptionsManager.MP_URL_PREFIX}.$prefix.mparticle.com", + baseClientImpl.getPodUrl( + NetworkOptionsManager.MP_URL_PREFIX, + prefix, + true + ) + ) + assertEquals( + "${NetworkOptionsManager.MP_IDENTITY_URL_PREFIX}.$prefix.mparticle.com", + baseClientImpl.getPodUrl( + NetworkOptionsManager.MP_IDENTITY_URL_PREFIX, + prefix, + true + ) + ) + + assertEquals( + "${NetworkOptionsManager.MP_URL_PREFIX}.mparticle.com", + baseClientImpl.getPodUrl( + NetworkOptionsManager.MP_URL_PREFIX, + prefix, + false + ) + ) + assertEquals( + "${NetworkOptionsManager.MP_IDENTITY_URL_PREFIX}.mparticle.com", + baseClientImpl.getPodUrl( + NetworkOptionsManager.MP_IDENTITY_URL_PREFIX, + prefix, + false + ) + ) + } + } + @Test @Throws(MalformedURLException::class) fun testGetUrlForceDefaultOption() { @@ -56,17 +218,160 @@ class MParticleBaseClientImplTest : BaseCleanInstallEachTest() { val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name, true) - Assert.assertEquals(defaultUrls[endpoint].toString(), generatedUrl.toString()) - TestCase.assertTrue(generatedUrl === generatedUrl.defaultUrl) + assertEquals(defaultUrls[endpoint].toString(), generatedUrl.defaultUrl.toString()) } for (endpoint in MParticleBaseClientImpl.Endpoint.values()) { val generatedUrl = baseClientImpl.getUrl(endpoint, endpoint.name, false) Assert.assertNotEquals(defaultUrls[endpoint].toString(), generatedUrl.toString()) Assert.assertFalse(generatedUrl === generatedUrl.defaultUrl) - Assert.assertEquals( + assertEquals( defaultUrls[endpoint].toString(), generatedUrl.defaultUrl.toString() ) } } + + @Test + @Throws(MalformedURLException::class) + fun testGenerateDefaultURL() { + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val subdirectory = "/v2/" + val uri = Uri.Builder() + .scheme(BuildConfig.SCHEME) + .encodedAuthority("nativesdks.us1.mparticle.com") + .path("$subdirectory us1-foo/events") + .build() + val result = baseClientImpl.generateDefaultURL( + false, + uri, + "nativesdks.mparticle.com", + "v2/us1-akshd324uajbhg123OIASI/events" + ) + assertEquals("https://nativesdks.mparticle.com/v2/us1-akshd324uajbhg123OIASI/events", result.toString()) + } + + @Test + @Throws(MalformedURLException::class) + fun testGenerateDefaultURL_When_defaultDomain_IS_Empty() { + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val subdirectory = "/v2/" + val uri = Uri.Builder() + .scheme(BuildConfig.SCHEME) + .encodedAuthority("nativesdks.us1.mparticle.com") + .path("$subdirectory eu1-fooapi/events") + .build() + val result = baseClientImpl.generateDefaultURL( + false, + uri, + "", + "v2/us1-asjdjasdgjhasgdjhas/events" + ) + assertEquals("https://nativesdks.us1.mparticle.com/v2/us1-asjdjasdgjhasgdjhas/events", result.toString()) + } + + @Test + @Throws(MalformedURLException::class) + fun testGenerateDefaultURL_When_defaultDomain_IS_NULL() { + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val subdirectory = "/v2/" + val uri = Uri.Builder() + .scheme(BuildConfig.SCHEME) + .encodedAuthority("nativesdks.us1.mparticle.com") + .path("$subdirectory us1-foo/events") + .build() + val result = baseClientImpl.generateDefaultURL( + false, + uri, + null, + "v2/us1-asjdjasdgjhasgdjhas/events" + ) + assertEquals("https://nativesdks.us1.mparticle.com/v2/us1-asjdjasdgjhasgdjhas/events", result.toString()) + } + + @Test + @Throws(MalformedURLException::class) + fun testGenerateDefaultURL_When_URL_IS_NULL() { + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val result = baseClientImpl.generateDefaultURL( + false, + null, + "nativesdks.mparticle.com", + "v2/us1-bee5781b649a7a40a592c2000bc892d0/events" + ) + assertEquals(null, result) + } + + @Test + @Throws(MalformedURLException::class) + fun testGenerateDefaultURL_When_PATH_IS_NULL() { + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val subdirectory = "/v2/" + val uri = Uri.Builder() + .scheme(BuildConfig.SCHEME) + .encodedAuthority("nativesdks.us1.mparticle.com") + .path("$subdirectory us1-foo/events") + .build() + val result = baseClientImpl.generateDefaultURL( + false, + uri, + "nativesdks.mparticle.com", + null + ) + assertEquals("https://nativesdks.mparticle.com/v2/%20us1-foo/events", result.toString()) + } + + @Test + @Throws(MalformedURLException::class) + fun testGenerateDefaultURL_When_defaultDomain_AND_URL_AND_PATH_ARE_NULL() { + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val result = baseClientImpl.generateDefaultURL(false, null, null, null) + assertEquals(null, result) + } + + @Test + @Throws(MalformedURLException::class) + fun testGenerateDefaultURL_When_defaultDomain_FLAG_IS_TRUE() { + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val subdirectory = "/v2/" + val uri = Uri.Builder() + .scheme(BuildConfig.SCHEME) + .encodedAuthority("nativesdks.us1.mparticle.com") + .path("$subdirectory us1-foo/events") + .build() + val result = baseClientImpl.generateDefaultURL( + true, + uri, + "nativesdks.mparticle.com", + "v2/us1-akshd324uajbhg123OIASI/events" + ) + assertEquals(null, result) + } } diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt index 70beceb25..4c35066df 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/NetworkOptionsTest.kt @@ -36,9 +36,10 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { @Throws(MalformedURLException::class, InterruptedException::class) fun testDefaultEndpoints() { MParticle.start(MParticleOptions.builder(mContext).credentials(apiKey, "s").build()) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl setClients() Assert.assertEquals( - NetworkOptionsManager.MP_URL, + baseClientImpl.getPodUrl(NetworkOptionsManager.MP_URL_PREFIX, "", false), mpClient.getUrl(MParticleBaseClientImpl.Endpoint.AUDIENCE).authority ) Assert.assertEquals( @@ -46,11 +47,15 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { mpClient.getUrl(MParticleBaseClientImpl.Endpoint.CONFIG).authority ) Assert.assertEquals( - NetworkOptionsManager.MP_URL, + baseClientImpl.getPodUrl(NetworkOptionsManager.MP_URL_PREFIX, "", false), mpClient.getUrl(MParticleBaseClientImpl.Endpoint.EVENTS).authority ) Assert.assertEquals( - NetworkOptionsManager.MP_IDENTITY_URL, + baseClientImpl.getPodUrl( + NetworkOptionsManager.MP_IDENTITY_URL_PREFIX, + "", + false + ), mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY).authority ) var randIdentityPath = mRandomUtils.getAlphaString(10) @@ -71,7 +76,7 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { identityClient.getUrl(MParticleBaseClientImpl.Endpoint.EVENTS).authority ) Assert.assertEquals( - NetworkOptionsManager.MP_IDENTITY_URL, + NetworkOptionsManager.MP_IDENTITY_URL_PREFIX.addSuffix(), identityClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY).authority ) randIdentityPath = mRandomUtils.getAlphaString(10) @@ -84,6 +89,8 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { ) } + private fun String.addSuffix(suffix: String = ".mparticle.com") = "$this$suffix" + @Test @Throws(MalformedURLException::class) fun testRandomEndpoint() { @@ -181,6 +188,130 @@ class NetworkOptionsTest : BaseCleanInstallEachTest() { ) } + // https://go.mparticle.com/work/SQDSDKS-6613 + @Test + @Throws(MalformedURLException::class) + fun testRandomEndpoint_With_DomainMapping_And_DirectURL_Routing_true() { + val identityUrl = mRandomUtils.getAlphaString(20) + val configUrl = mRandomUtils.getAlphaString(20) + val audienceUrl = mRandomUtils.getAlphaString(20) + val eventsUrl = mRandomUtils.getAlphaString(20) + val options = MParticleOptions.builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions.builder() + .addDomainMapping( + DomainMapping.audienceMapping(audienceUrl) + .build() + ) + .addDomainMapping( + DomainMapping.configMapping(configUrl) + .build() + ) + .addDomainMapping( + DomainMapping.identityMapping(identityUrl) + .build() + ) + .addDomainMapping(DomainMapping.eventsMapping(eventsUrl).build()) + .build() + ) + .build() + MParticle.start(options) + setClients() + Assert.assertEquals( + audienceUrl, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.AUDIENCE).authority + ) + Assert.assertEquals( + configUrl, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.CONFIG).authority + ) + Assert.assertEquals( + eventsUrl, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.EVENTS).authority + ) + Assert.assertEquals( + identityUrl, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY).authority + ) + var randIdentityPath = mRandomUtils.getAlphaString(10) + Assert.assertEquals( + "/v1/$randIdentityPath", + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, randIdentityPath).path + ) + Assert.assertEquals( + audienceUrl, + identityClient.getUrl(MParticleBaseClientImpl.Endpoint.AUDIENCE).authority + ) + Assert.assertEquals( + configUrl, + identityClient.getUrl(MParticleBaseClientImpl.Endpoint.CONFIG).authority + ) + Assert.assertEquals( + eventsUrl, + identityClient.getUrl(MParticleBaseClientImpl.Endpoint.EVENTS).authority + ) + Assert.assertEquals( + identityUrl, + identityClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY).authority + ) + randIdentityPath = mRandomUtils.getAlphaString(10) + Assert.assertEquals( + "/v1/$randIdentityPath", + identityClient.getUrl( + MParticleBaseClientImpl.Endpoint.IDENTITY, + randIdentityPath + ).path + ) + + // test the that the Path is still the default one (make sure the overrideSubdirectory is not kicking in when it shouldn't) + Assert.assertEquals( + defaultUrls[MParticleBaseClientImpl.Endpoint.AUDIENCE]?.path, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.AUDIENCE).path + ) + Assert.assertEquals( + defaultUrls[MParticleBaseClientImpl.Endpoint.CONFIG]?.path, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.CONFIG).path + ) + Assert.assertEquals( + defaultUrls[MParticleBaseClientImpl.Endpoint.EVENTS]?.path, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.EVENTS).path + ) + Assert.assertEquals( + defaultUrls[MParticleBaseClientImpl.Endpoint.IDENTITY]?.path, + mpClient.getUrl( + MParticleBaseClientImpl.Endpoint.IDENTITY, + MParticleBaseClientImpl.Endpoint.IDENTITY.name + ).path + ) + } + + @Test + @Throws(MalformedURLException::class) + fun testRandomEndpoint_when_No_Domain_Mapping_And_DirectURL_Routing_false() { + val options = MParticleOptions.builder(mContext) + .credentials("us1-14a65s4d65a4ds", "jasjgasgauysdkabmnx") + .build() + MParticle.start(options) + setClients() + Assert.assertEquals( + NetworkOptionsManager.MP_URL_PREFIX.addSuffix(), + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.AUDIENCE).authority + ) + Assert.assertEquals( + NetworkOptionsManager.MP_CONFIG_URL, + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.CONFIG).authority + ) + Assert.assertEquals( + NetworkOptionsManager.MP_URL_PREFIX.addSuffix(), + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.EVENTS).authority + ) + Assert.assertEquals( + NetworkOptionsManager.MP_IDENTITY_URL_PREFIX.addSuffix(), + mpClient.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY).authority + ) + } + @Test @Throws(MalformedURLException::class) fun testOverrideSubdirectory() { diff --git a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java index cf225f843..01d4fb628 100644 --- a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java +++ b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java @@ -51,6 +51,7 @@ public class ConfigManager { static final String KEY_OPT_OUT = "oo"; public static final String KEY_UNHANDLED_EXCEPTIONS = "cue"; public static final String KEY_PUSH_MESSAGES = "pmk"; + public static final String KEY_DIRECT_URL_ROUTING = "dur"; public static final String KEY_EMBEDDED_KITS = "eks"; static final String KEY_UPLOAD_INTERVAL = "uitl"; static final String KEY_SESSION_TIMEOUT = "stl"; @@ -83,6 +84,7 @@ public class ConfigManager { static SharedPreferences sPreferences; private static JSONArray sPushKeys; + private boolean directUrlRouting = false; private UserStorage mUserStorage; private String mLogUnhandledExceptions = VALUE_APP_DEFINED; @@ -406,6 +408,11 @@ private synchronized void updateCoreConfig(JSONObject responseJSON, boolean newC editor.putString(KEY_PUSH_MESSAGES, sPushKeys.toString()); } + if (responseJSON.has(KEY_DIRECT_URL_ROUTING)) { + directUrlRouting = responseJSON.optBoolean(KEY_DIRECT_URL_ROUTING); + editor.putBoolean(KEY_DIRECT_URL_ROUTING, directUrlRouting); + } + mRampValue = responseJSON.optInt(KEY_RAMP, -1); if (responseJSON.has(KEY_OPT_OUT)) { @@ -1276,6 +1283,29 @@ public ConsentState getConsentState(long mpid) { return ConsentState.withConsentState(serializedConsent).build(); } + public boolean isDirectUrlRoutingEnabled() { + return directUrlRouting; + } + + + /* This function is called to get the specific pod/silo prefix when the `directUrlRouting` is `true`. mParticle API keys are prefixed with the + silo and a hyphen (ex. "us1-", "us2-", "eu1-"). us1 was the first silo,and before other silos existed, there were no prefixes and all apiKeys + were us1. As such, if we split on a '-' and the resulting array length is 1, then it is an older APIkey that should route to us1. + When splitKey.length is greater than 1, then splitKey[0] will be us1, us2, eu1, au1, or st1, etc as new silos are added */ + public String getPodPrefix() { + String prefix = "us1"; + try { + String[] prefixFromApi = getApiKey().split("-"); + if (prefixFromApi.length > 1) { + prefix = prefixFromApi[0]; + } + } catch (Exception e) { + prefix = "us1"; + Logger.error("Error while getting pod prefix for direct URL routing : " + e); + } + return prefix; + } + public void setConsentState(ConsentState state, long mpid) { String serializedConsent = null; if (state != null) { diff --git a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java index 5615fe927..eb7a0cce4 100644 --- a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java +++ b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java @@ -23,10 +23,10 @@ public class MParticleBaseClientImpl implements MParticleBaseClient { - private Context mContext; - private ConfigManager mConfigManager; + private final Context mContext; + private final ConfigManager mConfigManager; private BaseNetworkConnection mRequestHandler; - private SharedPreferences mPreferences; + private final SharedPreferences mPreferences; String mApiKey; private static final String SERVICE_VERSION_1 = "/v1"; @@ -103,23 +103,42 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, boolean NetworkOptions networkOptions = mConfigManager.getNetworkOptions(); DomainMapping domainMapping = networkOptions.getDomain(endpoint); String url = NetworkOptionsManager.getDefaultUrl(endpoint); - boolean isDefaultUrl = true; - if (domainMapping != null && !MPUtility.isEmpty(domainMapping.getUrl()) && !forceDefaultUrl) { - String domainMappingUrl = domainMapping.getUrl(); - isDefaultUrl = url.equals(domainMappingUrl); + + // `defaultDomain` variable is for URL generation when domain mapping is specified. + String defaultDomain = url; + boolean isDefaultDomain = true; + + // Check if domain mapping is specified and update the URL based on domain mapping + String domainMappingUrl = domainMapping != null ? domainMapping.getUrl() : null; + if (!MPUtility.isEmpty(domainMappingUrl)) { + isDefaultDomain = url.equals(domainMappingUrl); url = domainMappingUrl; } + + if (endpoint != Endpoint.CONFIG) { + // Set URL with pod prefix if it’s the default domain and endpoint is not CONFIG + if (isDefaultDomain) { + url = getPodUrl(url, mConfigManager.getPodPrefix(), mConfigManager.isDirectUrlRoutingEnabled()); + } else { + // When domain mapping is specified, generate the default domain. Whether podRedirection is enabled or not, always use the original URL. + defaultDomain = getPodUrl(defaultDomain, null, false); + } + } + Uri uri; - MPUrl defaultUrl = !isDefaultUrl ? getUrl(endpoint, identityPath, true) : null; String subdirectory; - boolean overridesSubdirectory = domainMapping.isOverridesSubdirectory() && !forceDefaultUrl; + String pathPrefix; + String pathPostfix; + boolean overridesSubdirectory = domainMapping.isOverridesSubdirectory(); switch (endpoint) { case CONFIG: - subdirectory = overridesSubdirectory ? "" : SERVICE_VERSION_4 + "/"; + pathPrefix = SERVICE_VERSION_4 + "/"; + subdirectory = overridesSubdirectory ? "" : pathPrefix; + pathPostfix = mApiKey + "/config"; Uri.Builder builder = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) - .path(subdirectory + mApiKey + "/config") + .path(subdirectory + pathPostfix) .appendQueryParameter("av", MPUtility.getAppVersionName(mContext)) .appendQueryParameter("sv", Constants.MPARTICLE_VERSION); if (mConfigManager.getDataplanId() != null) { @@ -133,44 +152,86 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, boolean } } } - uri = builder.build(); - return MPUrl.getUrl(uri.toString(), defaultUrl); + return MPUrl.getUrl(builder.build().toString(), generateDefaultURL(isDefaultDomain, builder.build(), defaultDomain, (pathPrefix + pathPostfix))); case EVENTS: - subdirectory = overridesSubdirectory ? "" : SERVICE_VERSION_2 + "/"; + pathPrefix = SERVICE_VERSION_2 + "/"; + subdirectory = overridesSubdirectory ? "" : pathPrefix; + pathPostfix = mApiKey + "/events"; uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) - .path(subdirectory + mApiKey + "/events") + .path(subdirectory + pathPostfix) .build(); - return MPUrl.getUrl(uri.toString(), defaultUrl); + + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (pathPrefix + pathPostfix))); case ALIAS: - subdirectory = overridesSubdirectory ? "" : SERVICE_VERSION_1 + "/identity/"; + pathPrefix = SERVICE_VERSION_1 + "/identity/"; + subdirectory = overridesSubdirectory ? "" : pathPrefix; + pathPostfix = mApiKey + "/alias"; uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) - .path(subdirectory + mApiKey + "/alias") + .path(subdirectory + pathPostfix) .build(); - return MPUrl.getUrl(uri.toString(), defaultUrl); + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (pathPrefix + pathPostfix))); case IDENTITY: + pathPrefix = SERVICE_VERSION_1 + "/"; subdirectory = overridesSubdirectory ? "" : SERVICE_VERSION_1 + "/"; + pathPostfix = identityPath; uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) - .path(subdirectory + identityPath) + .path(subdirectory + pathPostfix) .build(); - return MPUrl.getUrl(uri.toString(), defaultUrl); + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (pathPrefix + pathPostfix))); case AUDIENCE: + pathPostfix = SERVICE_VERSION_2 + "/" + mApiKey + "/audience?mpID=" + mConfigManager.getMpid(); uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) - .path(SERVICE_VERSION_2 + "/" + mApiKey + "/audience?mpID=" + mConfigManager.getMpid()) + .path(pathPostfix) .build(); - return MPUrl.getUrl(uri.toString(), defaultUrl); + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, pathPostfix)); default: return null; } } + /** + * Generates a new URL using the default domain when domain mapping is specified. + * This method creates a new URI based on the existing URI, but replaces the domain with the default domain. + * + * @param isDefaultDomain If the default domain is true, return null without generating a new URL. If the default domain is false, generate and return a new URL. + * @param uri The existing URI which includes the domain mapping. + * @param defaultDomain The default domain name to be used in the new URI. + * @param path path to be used in the new URI. + * @return A new URI with the same path and scheme as the original URI, but with the default domain. + */ + protected MPUrl generateDefaultURL(boolean isDefaultDomain, Uri uri, String defaultDomain, String path) throws MalformedURLException { + if (isDefaultDomain) { + return null; + } + if (uri != null) { + Uri.Builder uriBuilder = Uri.parse(uri.toString()).buildUpon(); + if (defaultDomain != null && !defaultDomain.isEmpty()) { + uriBuilder.encodedAuthority(defaultDomain); + } + if (path != null && !path.isEmpty()) { + uriBuilder.path(path); + } + return MPUrl.getUrl(uriBuilder.build().toString(), null); + } + return null; + } + + String getPodUrl(String URLPrefix, String pod, boolean enablePodRedirection) { + if (URLPrefix != null) { + String newUrl = enablePodRedirection ? URLPrefix + "." + pod : URLPrefix; + return newUrl + ".mparticle.com"; + } + return null; + } + public enum Endpoint { CONFIG(1), IDENTITY(2), diff --git a/android-core/src/main/java/com/mparticle/networking/NetworkOptionsManager.java b/android-core/src/main/java/com/mparticle/networking/NetworkOptionsManager.java index 994aca1bf..b6ddb5c3d 100644 --- a/android-core/src/main/java/com/mparticle/networking/NetworkOptionsManager.java +++ b/android-core/src/main/java/com/mparticle/networking/NetworkOptionsManager.java @@ -11,8 +11,9 @@ public class NetworkOptionsManager { public static String MP_CONFIG_URL = "config2.mparticle.com"; - public static String MP_IDENTITY_URL = "identity.mparticle.com"; public static String MP_URL = "nativesdks.mparticle.com"; + public static String MP_IDENTITY_URL_PREFIX = "identity"; + public static String MP_URL_PREFIX = "nativesdks"; public static NetworkOptions validateAndResolve(NetworkOptions networkOptions) { if (networkOptions == null) { @@ -68,16 +69,17 @@ static List getDefaultCertificates() { } }; + // https://go.mparticle.com/work/SQDSDKS-6621 static String getDefaultUrl(Endpoint type) { switch (type) { case CONFIG: return MPUtility.isEmpty(BuildConfig.MP_CONFIG_URL) ? MP_CONFIG_URL : BuildConfig.MP_CONFIG_URL; case IDENTITY: - return MPUtility.isEmpty(BuildConfig.MP_IDENTITY_URL) ? MP_IDENTITY_URL : BuildConfig.MP_IDENTITY_URL; + return MPUtility.isEmpty(BuildConfig.MP_IDENTITY_URL) ? MP_IDENTITY_URL_PREFIX : BuildConfig.MP_IDENTITY_URL; case EVENTS: case ALIAS: case AUDIENCE: - return MPUtility.isEmpty(BuildConfig.MP_URL) ? MP_URL : BuildConfig.MP_URL; + return MPUtility.isEmpty(BuildConfig.MP_URL) ? MP_URL_PREFIX : BuildConfig.MP_URL; default: throw new IllegalArgumentException("Missing a Url for type " + type.name()); } diff --git a/testutils/src/main/java/com/mparticle/networking/MockServer.java b/testutils/src/main/java/com/mparticle/networking/MockServer.java index d83435e28..68ef5673e 100644 --- a/testutils/src/main/java/com/mparticle/networking/MockServer.java +++ b/testutils/src/main/java/com/mparticle/networking/MockServer.java @@ -503,7 +503,7 @@ public int compare(Map.Entry o1, Map.Entry o2) entry.getKey().url = getUrl(MParticleBaseClientImpl.Endpoint.CONFIG).getFile(); Logger.error("New Url: " + url + " -> " + entry.getKey().url); } - if (url.contains(NetworkOptionsManager.MP_URL) && !url.contains(apiKey)) { + if (url.contains(NetworkOptionsManager.MP_URL_PREFIX) && !url.contains(apiKey)) { entry.getKey().url = getUrl(MParticleBaseClientImpl.Endpoint.EVENTS).getFile(); } if (url.contains("/alias") && !url.contains(apiKey)) {