Skip to content

Commit c18c3a6

Browse files
authored
Remove ATB params from email pixels (#1354)
* Remove ATB params from email pixels * Use pixel enums rather than hardcoded strings for pixel names
1 parent 32f51da commit c18c3a6

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2021 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.app.global.api
18+
19+
import com.duckduckgo.app.pixels.AppPixelName
20+
import org.junit.Assert.assertEquals
21+
import org.junit.Before
22+
import org.junit.Test
23+
24+
class PixelAtbRemovalInterceptorTest {
25+
private lateinit var pixelAtbRemovalInterceptor: PixelAtbRemovalInterceptor
26+
27+
@Before
28+
fun setup() {
29+
pixelAtbRemovalInterceptor = PixelAtbRemovalInterceptor()
30+
}
31+
32+
@Test
33+
fun whenSendPixelTheRedactAtvInfoFromDefinedPixels() {
34+
AppPixelName.values().map { it.pixelName }.forEach { pixelName ->
35+
val pixelUrl = String.format(PIXEL_TEMPLATE, pixelName)
36+
val removalExpected = PixelAtbRemovalInterceptor.pixels.contains(pixelName)
37+
38+
val interceptedUrl = pixelAtbRemovalInterceptor.intercept(FakeChain(pixelUrl)).request.url
39+
assertEquals(removalExpected, interceptedUrl.queryParameter("atb") == null)
40+
}
41+
}
42+
43+
companion object {
44+
private const val PIXEL_TEMPLATE = "https://improving.duckduckgo.com/t/%s_android_phone?atb=v255-7zu&appVersion=5.74.0&test=1"
45+
}
46+
47+
}

app/src/main/java/com/duckduckgo/app/di/NetworkModule.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ import dagger.Module
4242
import dagger.Provides
4343
import kotlinx.coroutines.CoroutineScope
4444
import okhttp3.Cache
45+
import okhttp3.Interceptor
4546
import okhttp3.OkHttpClient
4647
import retrofit2.Retrofit
4748
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
4849
import retrofit2.converter.moshi.MoshiConverterFactory
4950
import retrofit2.converter.scalars.ScalarsConverterFactory
51+
import timber.log.Timber
5052
import java.io.File
5153
import javax.inject.Named
5254
import javax.inject.Singleton
@@ -72,10 +74,17 @@ class NetworkModule {
7274
fun pixelOkHttpClient(
7375
apiRequestInterceptor: ApiRequestInterceptor,
7476
pixelReQueryInterceptor: PixelReQueryInterceptor,
77+
pixelAtbRemovalInterceptor: PixelAtbRemovalInterceptor
7578
): OkHttpClient {
7679
return OkHttpClient.Builder()
7780
.addInterceptor(apiRequestInterceptor)
7881
.addInterceptor(pixelReQueryInterceptor)
82+
.addInterceptor(pixelAtbRemovalInterceptor)
83+
// shall be the last one as it is logging the pixel request url that goes out
84+
.addInterceptor { chain: Interceptor.Chain ->
85+
Timber.v("Pixel url request: ${chain.request().url}")
86+
return@addInterceptor chain.proceed(chain.request())
87+
}
7988
.build()
8089
}
8190

@@ -117,6 +126,11 @@ class NetworkModule {
117126
return PixelReQueryInterceptor()
118127
}
119128

129+
@Provides
130+
fun pixelAtbRemovalInterceptor(): PixelAtbRemovalInterceptor {
131+
return PixelAtbRemovalInterceptor()
132+
}
133+
120134
@Provides
121135
fun trackerListService(@Named("api") retrofit: Retrofit): TrackerListService =
122136
retrofit.create(TrackerListService::class.java)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2021 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.app.global.api
18+
19+
import androidx.annotation.VisibleForTesting
20+
import com.duckduckgo.app.global.AppUrl
21+
import com.duckduckgo.app.pixels.AppPixelName
22+
import okhttp3.Interceptor
23+
import okhttp3.Response
24+
25+
class PixelAtbRemovalInterceptor : Interceptor {
26+
override fun intercept(chain: Interceptor.Chain): Response {
27+
val request = chain.request().newBuilder()
28+
val pixel = chain.request().url.pathSegments.last()
29+
val url = if (pixels.contains(pixel.substringBefore("_android_"))) {
30+
chain.request().url.newBuilder().removeAllQueryParameters(AppUrl.ParamKey.ATB).build()
31+
} else {
32+
chain.request().url.newBuilder().build()
33+
}
34+
35+
return chain.proceed(request.url(url).build())
36+
}
37+
38+
companion object {
39+
// list of pixels for which we'll remove the ATB information
40+
@VisibleForTesting
41+
val pixels = listOf(
42+
AppPixelName.EMAIL_TOOLTIP_DISMISSED.pixelName,
43+
AppPixelName.EMAIL_USE_ALIAS.pixelName,
44+
AppPixelName.EMAIL_USE_ADDRESS.pixelName
45+
)
46+
}
47+
}

0 commit comments

Comments
 (0)