diff --git a/app/src/androidTest/java/com/duckduckgo/app/global/api/PixelAtbRemovalInterceptorTest.kt b/app/src/androidTest/java/com/duckduckgo/app/global/api/PixelAtbRemovalInterceptorTest.kt new file mode 100644 index 000000000000..f1c3aaa97482 --- /dev/null +++ b/app/src/androidTest/java/com/duckduckgo/app/global/api/PixelAtbRemovalInterceptorTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.app.global.api + +import com.duckduckgo.app.pixels.AppPixelName +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +class PixelAtbRemovalInterceptorTest { + private lateinit var pixelAtbRemovalInterceptor: PixelAtbRemovalInterceptor + + @Before + fun setup() { + pixelAtbRemovalInterceptor = PixelAtbRemovalInterceptor() + } + + @Test + fun whenSendPixelTheRedactAtvInfoFromDefinedPixels() { + AppPixelName.values().map { it.pixelName }.forEach { pixelName -> + val pixelUrl = String.format(PIXEL_TEMPLATE, pixelName) + val removalExpected = PixelAtbRemovalInterceptor.pixels.contains(pixelName) + + val interceptedUrl = pixelAtbRemovalInterceptor.intercept(FakeChain(pixelUrl)).request.url + assertEquals(removalExpected, interceptedUrl.queryParameter("atb") == null) + } + } + + companion object { + private const val PIXEL_TEMPLATE = "https://improving.duckduckgo.com/t/%s_android_phone?atb=v255-7zu&appVersion=5.74.0&test=1" + } + +} diff --git a/app/src/main/java/com/duckduckgo/app/di/NetworkModule.kt b/app/src/main/java/com/duckduckgo/app/di/NetworkModule.kt index 7e2ff0104f76..1ef6c5b7324e 100644 --- a/app/src/main/java/com/duckduckgo/app/di/NetworkModule.kt +++ b/app/src/main/java/com/duckduckgo/app/di/NetworkModule.kt @@ -42,11 +42,13 @@ import dagger.Module import dagger.Provides import kotlinx.coroutines.CoroutineScope import okhttp3.Cache +import okhttp3.Interceptor import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.converter.scalars.ScalarsConverterFactory +import timber.log.Timber import java.io.File import javax.inject.Named import javax.inject.Singleton @@ -72,10 +74,17 @@ class NetworkModule { fun pixelOkHttpClient( apiRequestInterceptor: ApiRequestInterceptor, pixelReQueryInterceptor: PixelReQueryInterceptor, + pixelAtbRemovalInterceptor: PixelAtbRemovalInterceptor ): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(apiRequestInterceptor) .addInterceptor(pixelReQueryInterceptor) + .addInterceptor(pixelAtbRemovalInterceptor) + // shall be the last one as it is logging the pixel request url that goes out + .addInterceptor { chain: Interceptor.Chain -> + Timber.v("Pixel url request: ${chain.request().url}") + return@addInterceptor chain.proceed(chain.request()) + } .build() } @@ -117,6 +126,11 @@ class NetworkModule { return PixelReQueryInterceptor() } + @Provides + fun pixelAtbRemovalInterceptor(): PixelAtbRemovalInterceptor { + return PixelAtbRemovalInterceptor() + } + @Provides fun trackerListService(@Named("api") retrofit: Retrofit): TrackerListService = retrofit.create(TrackerListService::class.java) diff --git a/app/src/main/java/com/duckduckgo/app/global/api/PixelAtbRemovalInterceptor.kt b/app/src/main/java/com/duckduckgo/app/global/api/PixelAtbRemovalInterceptor.kt new file mode 100644 index 000000000000..654bb1bd8b51 --- /dev/null +++ b/app/src/main/java/com/duckduckgo/app/global/api/PixelAtbRemovalInterceptor.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.app.global.api + +import androidx.annotation.VisibleForTesting +import com.duckduckgo.app.global.AppUrl +import com.duckduckgo.app.pixels.AppPixelName +import okhttp3.Interceptor +import okhttp3.Response + +class PixelAtbRemovalInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request().newBuilder() + val pixel = chain.request().url.pathSegments.last() + val url = if (pixels.contains(pixel.substringBefore("_android_"))) { + chain.request().url.newBuilder().removeAllQueryParameters(AppUrl.ParamKey.ATB).build() + } else { + chain.request().url.newBuilder().build() + } + + return chain.proceed(request.url(url).build()) + } + + companion object { + // list of pixels for which we'll remove the ATB information + @VisibleForTesting + val pixels = listOf( + AppPixelName.EMAIL_TOOLTIP_DISMISSED.pixelName, + AppPixelName.EMAIL_USE_ALIAS.pixelName, + AppPixelName.EMAIL_USE_ADDRESS.pixelName + ) + } +}