Skip to content

Commit a15a7ed

Browse files
authored
Generalize pixel-param removal (#3829)
Task/Issue URL: https://app.asana.com/0/488551667048375/1205898947038457/f ### Description Generalize the pixel param removal interceptor, migrate existing users and remove duplicate interceptors ### Steps to test this PR QA optional, just code review
1 parent cdfe0aa commit a15a7ed

File tree

10 files changed

+181
-345
lines changed

10 files changed

+181
-345
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2023 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.mobile.android.vpn.pixels
18+
19+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin
20+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin.PixelParameter
21+
import com.duckduckgo.di.scopes.AppScope
22+
import com.squareup.anvil.annotations.ContributesMultibinding
23+
import javax.inject.Inject
24+
25+
@ContributesMultibinding(AppScope::class)
26+
class VpnPixelParamRemovalPlugin @Inject constructor() : PixelParamRemovalPlugin {
27+
override fun names(): List<Pair<String, Set<PixelParameter>>> {
28+
return listOf(
29+
ATP_PIXEL_PREFIX to PixelParameter.removeAtb(),
30+
NETP_PIXEL_PREFIX to PixelParameter.removeAtb(),
31+
VPN_PIXEL_PREFIX to PixelParameter.removeAtb(),
32+
"m_atp_unprotected_apps_bucket_" to PixelParameter.removeAll(),
33+
)
34+
}
35+
36+
companion object {
37+
private const val ATP_PIXEL_PREFIX = "m_atp_"
38+
private const val NETP_PIXEL_PREFIX = "m_netp_"
39+
private const val VPN_PIXEL_PREFIX = "m_vpn_"
40+
}
41+
}

app/src/main/java/com/duckduckgo/app/global/api/AtpPixelRemovalInterceptor.kt

Lines changed: 0 additions & 69 deletions
This file was deleted.

app/src/main/java/com/duckduckgo/app/global/api/PixelAdClickAttributionRemovalInterceptor.kt

Lines changed: 9 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,63 +16,21 @@
1616

1717
package com.duckduckgo.app.global.api
1818

19-
import androidx.annotation.VisibleForTesting
2019
import com.duckduckgo.adclick.impl.pixels.AdClickPixelName
21-
import com.duckduckgo.app.global.AppUrl
22-
import com.duckduckgo.app.global.plugins.pixel.PixelInterceptorPlugin
20+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin
21+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin.PixelParameter
2322
import com.duckduckgo.di.scopes.AppScope
2423
import com.squareup.anvil.annotations.ContributesMultibinding
2524
import javax.inject.Inject
26-
import okhttp3.Interceptor
27-
import okhttp3.Response
2825

29-
@ContributesMultibinding(
30-
scope = AppScope::class,
31-
boundType = PixelInterceptorPlugin::class,
32-
)
33-
class PixelAdClickAttributionRemovalInterceptor @Inject constructor() : Interceptor, PixelInterceptorPlugin {
26+
@ContributesMultibinding(scope = AppScope::class)
27+
class PixelAdClickAttributionRemovalInterceptor @Inject constructor() : PixelParamRemovalPlugin {
3428

35-
override fun intercept(chain: Interceptor.Chain): Response {
36-
val request = chain.request().newBuilder()
37-
val pixel = chain.request().url.pathSegments.last()
38-
val pixelName = getPixelName(pixel)
39-
40-
val url = if (PIXELS_SET_NO_ATB.contains(pixelName)) {
41-
chain.request().url.newBuilder()
42-
.removeAllQueryParameters(AppUrl.ParamKey.ATB)
43-
.build()
44-
} else if (PIXELS_SET_NO_ATB_AND_VERSION.contains(pixelName)) {
45-
chain.request().url.newBuilder()
46-
.removeAllQueryParameters(AppUrl.ParamKey.ATB)
47-
.removeAllQueryParameters(APP_VERSION_PARAM)
48-
.build()
49-
} else {
50-
chain.request().url
51-
}
52-
53-
return chain.proceed(request.url(url).build())
54-
}
55-
56-
override fun getInterceptor(): Interceptor = this
57-
58-
private fun getPixelName(pixel: String): String {
59-
val suffixIndex = pixel.indexOf(PIXEL_PLATFORM_SUFFIX).takeIf { it > 0 } ?: 0
60-
return pixel.substring(0, suffixIndex)
61-
}
62-
63-
companion object {
64-
private const val PIXEL_PLATFORM_SUFFIX = "_android"
65-
private const val APP_VERSION_PARAM = "appVersion"
66-
67-
@VisibleForTesting
68-
internal val PIXELS_SET_NO_ATB = setOf(
69-
AdClickPixelName.AD_CLICK_DETECTED.pixelName,
70-
AdClickPixelName.AD_CLICK_ACTIVE.pixelName,
71-
)
72-
73-
@VisibleForTesting
74-
internal val PIXELS_SET_NO_ATB_AND_VERSION = setOf(
75-
AdClickPixelName.AD_CLICK_PAGELOADS_WITH_AD_ATTRIBUTION.pixelName,
29+
override fun names(): List<Pair<String, Set<PixelParameter>>> {
30+
return listOf(
31+
AdClickPixelName.AD_CLICK_DETECTED.pixelName to PixelParameter.removeAtb(),
32+
AdClickPixelName.AD_CLICK_ACTIVE.pixelName to PixelParameter.removeAtb(),
33+
AdClickPixelName.AD_CLICK_PAGELOADS_WITH_AD_ATTRIBUTION.pixelName to PixelParameter.removeAll(),
7634
)
7735
}
7836
}
Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ package com.duckduckgo.app.global.api
1919
import com.duckduckgo.app.global.AppUrl
2020
import com.duckduckgo.app.global.plugins.PluginPoint
2121
import com.duckduckgo.app.global.plugins.pixel.PixelInterceptorPlugin
22-
import com.duckduckgo.app.global.plugins.pixel.PixelRequiringDataCleaningPlugin
22+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin
23+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin.PixelParameter
24+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin.PixelParameter.APP_VERSION
25+
import com.duckduckgo.app.global.plugins.pixel.PixelParamRemovalPlugin.PixelParameter.ATB
2326
import com.duckduckgo.app.pixels.AppPixelName
2427
import com.duckduckgo.app.statistics.pixels.Pixel
2528
import com.duckduckgo.app.statistics.pixels.Pixel.StatisticsPixelName
@@ -34,49 +37,46 @@ import okhttp3.Response
3437
scope = AppScope::class,
3538
boundType = PixelInterceptorPlugin::class,
3639
)
37-
class AtbAndAppVersionPixelRemovalInterceptor @Inject constructor(
38-
private val pixelsPlugin: PluginPoint<PixelRequiringDataCleaningPlugin>,
40+
class PixelParamRemovalInterceptor @Inject constructor(
41+
private val pixelsPlugin: PluginPoint<PixelParamRemovalPlugin>,
3942
) : Interceptor, PixelInterceptorPlugin {
4043

41-
val pixels: Set<String> by lazy {
44+
val pixels by lazy {
4245
pixelsPlugin.getPlugins().flatMap { it.names() }.toSet()
4346
}
4447

4548
override fun intercept(chain: Interceptor.Chain): Response {
4649
val request = chain.request().newBuilder()
4750
val pixel = chain.request().url.pathSegments.last()
48-
val url = if (isInPixelsList(pixel)) {
49-
chain.request().url.newBuilder()
50-
.removeAllQueryParameters(AppUrl.ParamKey.ATB)
51-
.removeAllQueryParameters(Pixel.PixelParameter.APP_VERSION)
52-
.build()
53-
} else {
54-
chain.request().url.newBuilder().build()
55-
}
51+
val url = chain.request().url.newBuilder().apply {
52+
val atbs = pixels.filter { it.second.contains(ATB) }.map { it.first }
53+
val versions = pixels.filter { it.second.contains(APP_VERSION) }.map { it.first }
54+
if (atbs.any { pixel.startsWith(it) }) {
55+
removeAllQueryParameters(AppUrl.ParamKey.ATB)
56+
}
57+
if (versions.any { pixel.startsWith(it) }) {
58+
removeAllQueryParameters(Pixel.PixelParameter.APP_VERSION)
59+
}
60+
}.build()
5661

5762
return chain.proceed(request.url(url).build())
5863
}
5964

6065
override fun getInterceptor(): Interceptor {
6166
return this
6267
}
63-
64-
private fun isInPixelsList(pixel: String): Boolean {
65-
return pixels.firstOrNull { pixel.startsWith(it) } != null
66-
}
6768
}
6869

6970
@ContributesMultibinding(
7071
scope = AppScope::class,
71-
boundType = PixelRequiringDataCleaningPlugin::class,
72+
boundType = PixelParamRemovalPlugin::class,
7273
)
73-
object PixelInterceptorPixelsRequiringDataCleaning : PixelRequiringDataCleaningPlugin {
74-
override fun names(): List<String> {
74+
object PixelInterceptorPixelsRequiringDataCleaning : PixelParamRemovalPlugin {
75+
override fun names(): List<Pair<String, Set<PixelParameter>>> {
7576
return listOf(
76-
AppPixelName.EMAIL_COPIED_TO_CLIPBOARD.pixelName,
77-
StatisticsPixelName.BROWSER_DAILY_ACTIVE_FEATURE_STATE.pixelName,
78-
VoiceSearchPixelNames.VOICE_SEARCH_ERROR.pixelName,
79-
"m_atp_unprotected_apps_bucket_",
77+
AppPixelName.EMAIL_COPIED_TO_CLIPBOARD.pixelName to PixelParameter.removeAll(),
78+
StatisticsPixelName.BROWSER_DAILY_ACTIVE_FEATURE_STATE.pixelName to PixelParameter.removeAll(),
79+
VoiceSearchPixelNames.VOICE_SEARCH_ERROR.pixelName to PixelParameter.removeAll(),
8080
)
8181
}
8282
}

app/src/test/java/com/duckduckgo/app/global/api/AtbAndAppVersionPixelRemovalInterceptorTest.kt

Lines changed: 0 additions & 59 deletions
This file was deleted.

app/src/test/java/com/duckduckgo/app/global/api/AtpPixelRemovalInterceptorTest.kt

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)