[PM-20305] Migrate BaseUrlInterceptor and BaseUrlInterceptors to network module (#5068)

This commit is contained in:
Patrick Honkonen 2025-04-17 12:28:05 -04:00 committed by GitHub
parent f23079b5ac
commit 3722a45359
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 257 additions and 255 deletions

View File

@ -1,6 +1,8 @@
package com.x8bit.bitwarden.data.platform.datasource.network.di
import com.bitwarden.network.interceptor.AuthTokenInterceptor
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.BaseUrlsProvider
import com.bitwarden.network.interceptor.HeadersInterceptor
import com.bitwarden.network.service.ConfigService
import com.bitwarden.network.service.ConfigServiceImpl
@ -11,7 +13,6 @@ import com.bitwarden.network.service.PushServiceImpl
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.manager.AuthTokenManager
import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.RefreshAuthenticator
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.x8bit.bitwarden.data.platform.datasource.network.retrofit.Retrofits
import com.x8bit.bitwarden.data.platform.datasource.network.retrofit.RetrofitsImpl
import com.x8bit.bitwarden.data.platform.datasource.network.ssl.SslManager
@ -92,6 +93,13 @@ object PlatformNetworkModule {
environmentRepository = environmentRepository,
)
@Provides
@Singleton
fun providesBaseUrlInterceptors(
baseUrlsProvider: BaseUrlsProvider,
): BaseUrlInterceptors =
BaseUrlInterceptors(baseUrlsProvider = baseUrlsProvider)
@Provides
@Singleton
fun provideRetrofits(

View File

@ -1,44 +0,0 @@
package com.x8bit.bitwarden.data.platform.datasource.network.interceptor
import com.bitwarden.core.annotation.OmitFromCoverage
import com.bitwarden.data.repository.model.Environment
import com.bitwarden.data.repository.util.baseApiUrl
import com.bitwarden.data.repository.util.baseEventsUrl
import com.bitwarden.data.repository.util.baseIdentityUrl
import com.bitwarden.data.repository.util.toEnvironmentUrlsOrDefault
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import javax.inject.Inject
import javax.inject.Singleton
/**
* An overall container for various [BaseUrlInterceptor] implementations for different API groups.
*/
@OmitFromCoverage
@Singleton
class BaseUrlInterceptors @Inject constructor(
private val environmentDiskSource: EnvironmentDiskSource,
) {
private val environment: Environment
get() = environmentDiskSource.preAuthEnvironmentUrlData.toEnvironmentUrlsOrDefault()
/**
* An interceptor for "/api" calls.
*/
val apiInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
environment.environmentUrlData.baseApiUrl
}
/**
* An interceptor for "/identity" calls.
*/
val identityInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
environment.environmentUrlData.baseIdentityUrl
}
/**
* An interceptor for "/events" calls.
*/
val eventsInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
environment.environmentUrlData.baseEventsUrl
}
}

View File

@ -1,6 +1,6 @@
package com.x8bit.bitwarden.data.platform.datasource.network.retrofit
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import retrofit2.Retrofit
import retrofit2.http.Url

View File

@ -2,11 +2,11 @@ package com.x8bit.bitwarden.data.platform.datasource.network.retrofit
import com.bitwarden.network.core.NetworkResultCallAdapterFactory
import com.bitwarden.network.interceptor.AuthTokenInterceptor
import com.bitwarden.network.interceptor.BaseUrlInterceptor
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.HeadersInterceptor
import com.bitwarden.network.util.HEADER_KEY_AUTHORIZATION
import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.RefreshAuthenticator
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptor
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.x8bit.bitwarden.data.platform.datasource.network.ssl.SslManager
import com.x8bit.bitwarden.data.platform.util.isDevBuild
import kotlinx.serialization.json.Json

View File

@ -0,0 +1,34 @@
package com.x8bit.bitwarden.data.platform.provider
import com.bitwarden.data.repository.util.baseApiUrl
import com.bitwarden.data.repository.util.baseEventsUrl
import com.bitwarden.data.repository.util.baseIdentityUrl
import com.bitwarden.data.repository.util.toEnvironmentUrlsOrDefault
import com.bitwarden.network.interceptor.BaseUrlsProvider
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
/**
* The default implementation of [BaseUrlsProvider].
*/
class BaseUrlsProviderImpl(
private val environmentDiskSource: EnvironmentDiskSource,
) : BaseUrlsProvider {
override fun getBaseApiUrl(): String = environmentDiskSource
.preAuthEnvironmentUrlData
.toEnvironmentUrlsOrDefault()
.environmentUrlData
.baseApiUrl
override fun getBaseIdentityUrl(): String = environmentDiskSource
.preAuthEnvironmentUrlData
.toEnvironmentUrlsOrDefault()
.environmentUrlData
.baseIdentityUrl
override fun getBaseEventsUrl(): String = environmentDiskSource
.preAuthEnvironmentUrlData
.toEnvironmentUrlsOrDefault()
.environmentUrlData
.baseEventsUrl
}

View File

@ -0,0 +1,25 @@
package com.x8bit.bitwarden.data.platform.provider.di
import com.bitwarden.network.interceptor.BaseUrlsProvider
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.provider.BaseUrlsProviderImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
/**
* This module initializes platform-specific "Provider" dependencies for the application.
*/
@Module
@InstallIn(SingletonComponent::class)
object PlatformProvidersModule {
@Provides
@Singleton
fun provideBaseUrlsProvider(
environmentDiskSource: EnvironmentDiskSource,
): BaseUrlsProvider =
BaseUrlsProviderImpl(environmentDiskSource = environmentDiskSource)
}

View File

@ -1,10 +1,10 @@
package com.x8bit.bitwarden.data.platform.datasource.network.retrofit
import com.bitwarden.network.interceptor.AuthTokenInterceptor
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.HeadersInterceptor
import com.bitwarden.network.model.NetworkResult
import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.RefreshAuthenticator
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.x8bit.bitwarden.data.platform.datasource.network.ssl.SslManager
import com.x8bit.bitwarden.data.util.mockBuilder
import io.mockk.every

View File

@ -0,0 +1,69 @@
package com.x8bit.bitwarden.data.platform.manager.provider
import com.bitwarden.data.repository.model.Environment
import com.x8bit.bitwarden.data.platform.datasource.disk.FakeEnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.provider.BaseUrlsProviderImpl
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
class BaseUrlsProviderTest {
private val fakeEnvironmentDiskSource = FakeEnvironmentDiskSource()
private val baseUrlsManager = BaseUrlsProviderImpl(
environmentDiskSource = fakeEnvironmentDiskSource,
)
@Test
fun `getBaseApiUrl should return correct api URL when preAuthEnvironmentUrlData is set`() {
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = Environment.Eu.environmentUrlData
Assertions.assertEquals(
"https://vault.bitwarden.eu/api",
baseUrlsManager.getBaseApiUrl(),
)
}
@Test
fun `getBaseApiUrl should return default value when preAuthEnvironmentUrlData is null`() {
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = null
Assertions.assertEquals(
"https://vault.bitwarden.com/api",
baseUrlsManager.getBaseApiUrl(),
)
}
@Test
fun `getBaseIdentityUrl should return correct api URL when preAuthEnvironmentUrlData is set`() {
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = Environment.Eu.environmentUrlData
Assertions.assertEquals(
"https://vault.bitwarden.eu/identity",
baseUrlsManager.getBaseIdentityUrl(),
)
}
@Test
fun `getBaseIdentityUrl should return default value when preAuthEnvironmentUrlData is null`() {
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = null
Assertions.assertEquals(
"https://vault.bitwarden.com/identity",
baseUrlsManager.getBaseIdentityUrl(),
)
}
@Test
fun `getBaseEventsUrl should return correct api URL when preAuthEnvironmentUrlData is set`() {
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = Environment.Eu.environmentUrlData
Assertions.assertEquals(
"https://vault.bitwarden.eu/events",
baseUrlsManager.getBaseEventsUrl(),
)
}
@Test
fun `getBaseEventsUrl should return default value when preAuthEnvironmentUrlData is null`() {
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = null
Assertions.assertEquals(
"https://vault.bitwarden.com/events",
baseUrlsManager.getBaseEventsUrl(),
)
}
}

View File

@ -1,11 +1,12 @@
package com.bitwarden.authenticator.data.platform.datasource.network.di
import com.bitwarden.authenticator.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.bitwarden.authenticator.data.platform.datasource.network.retrofit.Retrofits
import com.bitwarden.authenticator.data.platform.datasource.network.retrofit.RetrofitsImpl
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_VALUE_CLIENT_NAME
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_VALUE_CLIENT_VERSION
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_VALUE_USER_AGENT
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.BaseUrlsProvider
import com.bitwarden.network.interceptor.HeadersInterceptor
import com.bitwarden.network.service.ConfigService
import com.bitwarden.network.service.ConfigServiceImpl
@ -38,6 +39,13 @@ object PlatformNetworkModule {
clientVersion = HEADER_VALUE_CLIENT_VERSION,
)
@Provides
@Singleton
fun providesBaseUrlInterceptors(
baseUrlsProvider: BaseUrlsProvider,
): BaseUrlInterceptors =
BaseUrlInterceptors(baseUrlsProvider = baseUrlsProvider)
@Provides
@Singleton
fun provideRetrofits(

View File

@ -1,58 +0,0 @@
package com.bitwarden.authenticator.data.platform.datasource.network.interceptor
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.Response
/**
* A [Interceptor] that optionally takes the current base URL of a request and replaces it with
* the currently set [baseUrl]
*/
class BaseUrlInterceptor : Interceptor {
/**
* The base URL to use as an override, or `null` if no override should be performed.
*/
var baseUrl: String? = null
set(value) {
field = value
baseHttpUrl = baseUrl?.let { requireNotNull(it.toHttpUrlOrNull()) }
}
private var baseHttpUrl: HttpUrl? = null
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// If no base URL is set, we can simply skip
val base = baseHttpUrl ?: return chain.proceed(request)
// Update the base URL used.
return chain.proceed(
request
.newBuilder()
.url(
request
.url
.replaceBaseUrlWith(base),
)
.build(),
)
}
}
/**
* Given a [HttpUrl], replaces the existing base URL with the given [baseUrl].
*/
private fun HttpUrl.replaceBaseUrlWith(
baseUrl: HttpUrl,
) = baseUrl
.newBuilder()
.addEncodedPathSegments(
this
.encodedPathSegments
.joinToString(separator = "/"),
)
.encodedQuery(this.encodedQuery)
.build()

View File

@ -1,33 +0,0 @@
package com.bitwarden.authenticator.data.platform.datasource.network.interceptor
import com.bitwarden.data.repository.model.Environment
import com.bitwarden.data.repository.util.baseApiUrl
import javax.inject.Inject
import javax.inject.Singleton
/**
* An overall container for various [BaseUrlInterceptor] implementations for different API groups.
*/
@Singleton
class BaseUrlInterceptors @Inject constructor() {
var environment: Environment = Environment.Us
set(value) {
field = value
updateBaseUrls(environment = value)
}
/**
* An interceptor for "/api" calls.
*/
val apiInterceptor: BaseUrlInterceptor = BaseUrlInterceptor()
init {
// Ensure all interceptors begin with a default value
environment = Environment.Us
}
private fun updateBaseUrls(environment: Environment) {
val environmentUrlData = environment.environmentUrlData
apiInterceptor.baseUrl = environmentUrlData.baseApiUrl
}
}

View File

@ -1,6 +1,6 @@
package com.bitwarden.authenticator.data.platform.datasource.network.retrofit
import com.bitwarden.authenticator.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import retrofit2.Retrofit
import retrofit2.http.Url

View File

@ -1,9 +1,9 @@
package com.bitwarden.authenticator.data.platform.datasource.network.retrofit
import com.bitwarden.authenticator.data.platform.datasource.network.interceptor.BaseUrlInterceptor
import com.bitwarden.authenticator.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.HeadersInterceptor
import com.bitwarden.network.core.NetworkResultCallAdapterFactory
import com.bitwarden.network.interceptor.BaseUrlInterceptor
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.HeadersInterceptor
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient

View File

@ -0,0 +1,23 @@
package com.bitwarden.authenticator.data.platform.provider
import com.bitwarden.core.annotation.OmitFromCoverage
import com.bitwarden.data.repository.model.Environment
import com.bitwarden.data.repository.util.baseApiUrl
import com.bitwarden.data.repository.util.baseEventsUrl
import com.bitwarden.data.repository.util.baseIdentityUrl
import com.bitwarden.network.interceptor.BaseUrlsProvider
/**
* Default implementation of [BaseUrlsProvider].
*/
@OmitFromCoverage
object BaseUrlsProviderImpl : BaseUrlsProvider {
override fun getBaseApiUrl(): String =
Environment.Us.environmentUrlData.baseApiUrl
override fun getBaseIdentityUrl(): String =
Environment.Us.environmentUrlData.baseIdentityUrl
override fun getBaseEventsUrl(): String =
Environment.Us.environmentUrlData.baseEventsUrl
}

View File

@ -0,0 +1,21 @@
package com.bitwarden.authenticator.data.platform.provider.di
import com.bitwarden.authenticator.data.platform.provider.BaseUrlsProviderImpl
import com.bitwarden.network.interceptor.BaseUrlsProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
/**
* Module responsible for providing platform provider dependencies.
*/
@Module
@InstallIn(SingletonComponent::class)
object PlatformProviderModule {
@Provides
@Singleton
fun provideBaseUrlsProvider(): BaseUrlsProvider = BaseUrlsProviderImpl
}

View File

@ -1,37 +0,0 @@
package com.bitwarden.authenticator.data.platform.datasource.network.interceptor
import com.bitwarden.network.interceptor.FakeInterceptorChain
import okhttp3.Request
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Test
class BaseUrlInterceptorTest {
private val baseUrlInterceptor = BaseUrlInterceptor()
@Test
fun `intercept with a null base URL should proceed with the original request`() {
val request = Request.Builder().url("http://www.fake.com/").build()
val chain = FakeInterceptorChain(request)
val response = baseUrlInterceptor.intercept(chain)
assertEquals(request, response.request)
assertEquals("http", response.request.url.scheme)
assertEquals("www.fake.com", response.request.url.host)
}
@Test
fun `intercept with a non-null base URL should update the base URL used by the request`() {
baseUrlInterceptor.baseUrl = "https://api.bitwarden.com"
val request = Request.Builder().url("http://www.fake.com/").build()
val chain = FakeInterceptorChain(request)
val response = baseUrlInterceptor.intercept(chain)
assertNotEquals(request, response.request)
assertEquals("https", response.request.url.scheme)
assertEquals("api.bitwarden.com", response.request.url.host)
}
}

View File

@ -1,69 +0,0 @@
package com.bitwarden.authenticator.data.platform.datasource.network.interceptor
import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson
import com.bitwarden.data.repository.model.Environment
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class BaseUrlInterceptorsTest {
private val baseUrlInterceptors = BaseUrlInterceptors()
@Test
fun `the default environment should be US and all interceptors should have the correct URLs`() {
assertEquals(
Environment.Us,
baseUrlInterceptors.environment,
)
assertEquals(
"https://vault.bitwarden.com/api",
baseUrlInterceptors.apiInterceptor.baseUrl,
)
}
@Suppress("MaxLineLength")
@Test
fun `setting the environment should update all the interceptors correctly for a non-blank base URL`() {
baseUrlInterceptors.environment = Environment.Eu
assertEquals(
"https://vault.bitwarden.eu/api",
baseUrlInterceptors.apiInterceptor.baseUrl,
)
}
@Suppress("MaxLineLength")
@Test
fun `setting the environment should update all the interceptors correctly for a blank base URL and all URLs filled`() {
baseUrlInterceptors.environment = Environment.SelfHosted(
environmentUrlData = EnvironmentUrlDataJson(
base = " ",
api = "https://api.com",
identity = "https://identity.com",
events = "https://events.com",
),
)
assertEquals(
"https://api.com",
baseUrlInterceptors.apiInterceptor.baseUrl,
)
}
@Suppress("MaxLineLength")
@Test
fun `setting the environment should update all the interceptors correctly for a blank base URL and some or all URLs absent`() {
baseUrlInterceptors.environment = Environment.SelfHosted(
environmentUrlData = EnvironmentUrlDataJson(
base = " ",
api = "",
identity = "",
icon = " ",
),
)
assertEquals(
"https://api.bitwarden.com",
baseUrlInterceptors.apiInterceptor.baseUrl,
)
}
}

View File

@ -1,6 +1,6 @@
package com.bitwarden.authenticator.data.platform.datasource.network.retrofit
import com.bitwarden.authenticator.data.platform.datasource.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.BaseUrlInterceptors
import com.bitwarden.network.interceptor.HeadersInterceptor
import com.bitwarden.network.model.NetworkResult
import io.mockk.every

View File

@ -1,4 +1,4 @@
package com.x8bit.bitwarden.data.platform.datasource.network.interceptor
package com.bitwarden.network.interceptor
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull

View File

@ -0,0 +1,35 @@
package com.bitwarden.network.interceptor
import com.bitwarden.core.annotation.OmitFromCoverage
import javax.inject.Inject
import javax.inject.Singleton
/**
* An overall container for various [BaseUrlInterceptor] implementations for different API groups.
*/
@OmitFromCoverage
@Singleton
class BaseUrlInterceptors @Inject constructor(
private val baseUrlsProvider: BaseUrlsProvider,
) {
/**
* An interceptor for "/api" calls.
*/
val apiInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
baseUrlsProvider.getBaseApiUrl()
}
/**
* An interceptor for "/identity" calls.
*/
val identityInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
baseUrlsProvider.getBaseIdentityUrl()
}
/**
* An interceptor for "/events" calls.
*/
val eventsInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
baseUrlsProvider.getBaseEventsUrl()
}
}

View File

@ -0,0 +1,21 @@
package com.bitwarden.network.interceptor
/**
* Provides base URLs for different API groups.
*/
interface BaseUrlsProvider {
/**
* Gets the base URL for "/api" calls.
*/
fun getBaseApiUrl(): String
/**
* Gets the base URL for "/identity" calls.
*/
fun getBaseIdentityUrl(): String
/**
* Gets the base URL for "/events" calls.
*/
fun getBaseEventsUrl(): String
}

View File

@ -1,6 +1,5 @@
package com.x8bit.bitwarden.data.platform.datasource.network.interceptor
package com.bitwarden.network.interceptor
import com.bitwarden.network.interceptor.FakeInterceptorChain
import okhttp3.Request
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotEquals