PM-35273: feat: Add support for SDK API calls by providing base urls (#6805)

This commit is contained in:
David Perez
2026-04-20 14:21:39 +00:00
committed by GitHub
parent 75b87e134c
commit 6e16daf001
17 changed files with 270 additions and 75 deletions
@@ -27,25 +27,21 @@ class AuthSdkSourceImpl(
override suspend fun getNewAuthRequest(
email: String,
): Result<AuthRequestResponse> = runCatchingWithLogs {
getClient()
.auth()
.newAuthRequest(
email = email.lowercase(),
)
useClient { auth().newAuthRequest(email = email.lowercase()) }
}
override suspend fun getUserFingerprint(
email: String,
publicKey: String,
): Result<String> = runCatchingWithLogs {
getClient()
.platform()
.fingerprint(
useClient {
platform().fingerprint(
req = FingerprintRequest(
fingerprintMaterial = email.lowercase(),
publicKey = publicKey,
),
)
}
}
override suspend fun hashPassword(
@@ -54,21 +50,19 @@ class AuthSdkSourceImpl(
kdf: Kdf,
purpose: HashPurpose,
): Result<String> = runCatchingWithLogs {
getClient()
.auth()
.hashPassword(
useClient {
auth().hashPassword(
email = email,
password = password,
kdfParams = kdf,
purpose = purpose,
)
}
}
override suspend fun makeKeyConnectorKeys(): Result<KeyConnectorResponse> =
runCatchingWithLogs {
getClient()
.auth()
.makeKeyConnectorKeys()
useClient { auth().makeKeyConnectorKeys() }
}
override suspend fun makeRegisterKeys(
@@ -76,13 +70,13 @@ class AuthSdkSourceImpl(
password: String,
kdf: Kdf,
): Result<RegisterKeyResponse> = runCatchingWithLogs {
getClient()
.auth()
.makeRegisterKeys(
useClient {
auth().makeRegisterKeys(
email = email,
password = password,
kdf = kdf,
)
}
}
override suspend fun makeRegisterTdeKeysAndUnlockVault(
@@ -105,15 +99,16 @@ class AuthSdkSourceImpl(
password: String,
additionalInputs: List<String>,
): Result<PasswordStrength> = runCatchingWithLogs {
@Suppress("UnsafeCallOnNullableType")
getClient()
.auth()
.passwordStrength(
password = password,
email = email,
additionalInputs = additionalInputs,
)
.toPasswordStrengthOrNull()!!
useClient {
@Suppress("UnsafeCallOnNullableType")
auth()
.passwordStrength(
password = password,
email = email,
additionalInputs = additionalInputs,
)
.toPasswordStrengthOrNull()!!
}
}
override suspend fun satisfiesPolicy(
@@ -121,12 +116,12 @@ class AuthSdkSourceImpl(
passwordStrength: PasswordStrength,
policy: MasterPasswordPolicyOptions,
): Result<Boolean> = runCatchingWithLogs {
getClient()
.auth()
.satisfiesPolicy(
useClient {
auth().satisfiesPolicy(
password = password,
strength = passwordStrength.toUByte(),
policy = policy,
)
}
}
}
@@ -50,7 +50,7 @@ object PlatformNetworkModule {
@Provides
@Singleton
fun provideBitwardenServiceClient(
fun provideBitwardenServiceClientConfig(
authTokenManager: AuthTokenManager,
baseUrlsProvider: BaseUrlsProvider,
authDiskSource: AuthDiskSource,
@@ -58,20 +58,26 @@ object PlatformNetworkModule {
buildInfoManager: BuildInfoManager,
networkCookieManager: NetworkCookieManager,
clock: Clock,
): BitwardenServiceClient = bitwardenServiceClient(
BitwardenServiceClientConfig(
clock = clock,
appIdProvider = authDiskSource,
clientData = BitwardenServiceClientConfig.ClientData(
userAgent = HEADER_VALUE_USER_AGENT,
clientName = HEADER_VALUE_CLIENT_NAME,
clientVersion = HEADER_VALUE_CLIENT_VERSION,
),
authTokenProvider = authTokenManager,
baseUrlsProvider = baseUrlsProvider,
certificateProvider = certificateManager,
enableHttpBodyLogging = buildInfoManager.isDevBuild,
cookieProvider = networkCookieManager,
): BitwardenServiceClientConfig = BitwardenServiceClientConfig(
clock = clock,
appIdProvider = authDiskSource,
clientData = BitwardenServiceClientConfig.ClientData(
userAgent = HEADER_VALUE_USER_AGENT,
clientName = HEADER_VALUE_CLIENT_NAME,
clientVersion = HEADER_VALUE_CLIENT_VERSION,
),
authTokenProvider = authTokenManager,
baseUrlsProvider = baseUrlsProvider,
certificateProvider = certificateManager,
enableHttpBodyLogging = buildInfoManager.isDevBuild,
cookieProvider = networkCookieManager,
)
@Provides
@Singleton
fun provideBitwardenServiceClient(
serviceClientConfig: BitwardenServiceClientConfig,
): BitwardenServiceClient = bitwardenServiceClient(
config = serviceClientConfig,
)
}
@@ -15,9 +15,22 @@ abstract class BaseSdkSource(
* Helper function to retrieve the [Client] associated with the given [userId].
*/
protected suspend fun getClient(
userId: String? = null,
userId: String,
): Client = sdkClientManager.getOrCreateClient(userId = userId)
/**
* Helper function to retrieve a new [Client] and use it in the given [block].
*/
protected suspend fun <T> useClient(
userId: String? = null,
accessToken: String? = null,
block: suspend Client.() -> T,
): T = sdkClientManager.singleUseClient(
userId = userId,
accessToken = accessToken,
block = block,
)
/**
* Invokes the [block] with `this` value as its receiver and returns its result if it was
* successful and catches any exception that was thrown from the `block` and wrapping it as a
@@ -11,7 +11,20 @@ interface SdkClientManager {
* Returns the cached [Client] instance for the given [userId], otherwise creates and caches
* a new one and returns it.
*/
suspend fun getOrCreateClient(userId: String?): Client
suspend fun getOrCreateClient(userId: String): Client
/**
* Helper function to retrieve a new instance of the [Client] and use it in the given [block].
* This client is never persisted after the [block] completes.
*
* @param userId The used to create the [Client]. If null, the SDK is unassociated with a user.
* @param accessToken The access token used in network requests.
*/
suspend fun <T> singleUseClient(
userId: String? = null,
accessToken: String? = null,
block: suspend Client.() -> T,
): T
/**
* Clears any resources from the [Client] associated with the given [userId] and removes it
@@ -15,10 +15,16 @@ class SdkClientManagerImpl(
sdkRepoFactory: SdkRepositoryFactory,
sdkPlatformApiFactory: SdkPlatformApiFactory,
private val featureFlagManager: FeatureFlagManager,
private val clientProvider: suspend (userId: String?) -> Client = { userId ->
private val clientProvider: suspend (
userId: String?,
accessToken: String?,
) -> Client = { userId, accessToken ->
Client(
tokenProvider = sdkRepoFactory.getClientManagedTokens(userId = userId),
settings = null,
tokenProvider = sdkRepoFactory.getClientManagedTokens(
userId = userId,
accessToken = accessToken,
),
settings = sdkRepoFactory.getClientSettings(),
)
.apply {
platform().loadFlags(featureFlagManager.sdkFeatureFlags)
@@ -32,7 +38,7 @@ class SdkClientManagerImpl(
}
},
) : SdkClientManager {
private val userIdToClientMap = mutableMapOf<String?, Client>()
private val userIdToClientMap = mutableMapOf<String, Client>()
init {
// The SDK requires access to Android APIs that were not made public until API 31. In order
@@ -44,8 +50,14 @@ class SdkClientManagerImpl(
}
override suspend fun getOrCreateClient(
userId: String,
): Client = userIdToClientMap.getOrPut(key = userId) { clientProvider(userId, null) }
override suspend fun <T> singleUseClient(
userId: String?,
): Client = userIdToClientMap.getOrPut(key = userId) { clientProvider(userId) }
accessToken: String?,
block: suspend Client.() -> T,
): T = clientProvider(userId, accessToken).use { it.block() }
override fun destroyClient(
userId: String?,
@@ -15,6 +15,7 @@ import com.bitwarden.data.datasource.disk.ConfigDiskSource
import com.bitwarden.data.manager.NativeLibraryManager
import com.bitwarden.data.repository.ServerConfigRepository
import com.bitwarden.network.BitwardenServiceClient
import com.bitwarden.network.model.BitwardenServiceClientConfig
import com.bitwarden.network.service.EventService
import com.bitwarden.network.service.PushService
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
@@ -367,11 +368,13 @@ object PlatformManagerModule {
cookieDiskSource: CookieDiskSource,
configDiskSource: ConfigDiskSource,
authDiskSource: AuthDiskSource,
serviceClientConfig: BitwardenServiceClientConfig,
): SdkRepositoryFactory = SdkRepositoryFactoryImpl(
vaultDiskSource = vaultDiskSource,
cookieDiskSource = cookieDiskSource,
configDiskSource = configDiskSource,
authDiskSource = authDiskSource,
serviceClientConfig = serviceClientConfig,
)
@Provides
@@ -1,6 +1,7 @@
package com.x8bit.bitwarden.data.platform.manager.sdk
import com.bitwarden.core.ClientManagedTokens
import com.bitwarden.core.ClientSettings
import com.bitwarden.sdk.Repositories
import com.bitwarden.sdk.ServerCommunicationConfigRepository
@@ -16,7 +17,15 @@ interface SdkRepositoryFactory {
/**
* Retrieves or creates a [ClientManagedTokens] for use with the Bitwarden SDK.
*/
fun getClientManagedTokens(userId: String?): ClientManagedTokens
fun getClientManagedTokens(
userId: String?,
accessToken: String?,
): ClientManagedTokens
/**
* Retrieves or creates a [ClientSettings] for use with the Bitwarden SDK.
*/
fun getClientSettings(): ClientSettings
/**
* Retrieves or creates a [ServerCommunicationConfigRepository] for use with the Bitwarden SDK.
@@ -1,7 +1,10 @@
package com.x8bit.bitwarden.data.platform.manager.sdk
import com.bitwarden.core.ClientManagedTokens
import com.bitwarden.core.ClientSettings
import com.bitwarden.core.DeviceType
import com.bitwarden.data.datasource.disk.ConfigDiskSource
import com.bitwarden.network.model.BitwardenServiceClientConfig
import com.bitwarden.sdk.Repositories
import com.bitwarden.sdk.ServerCommunicationConfigRepository
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
@@ -20,6 +23,7 @@ class SdkRepositoryFactoryImpl(
private val cookieDiskSource: CookieDiskSource,
private val configDiskSource: ConfigDiskSource,
private val authDiskSource: AuthDiskSource,
private val serviceClientConfig: BitwardenServiceClientConfig,
) : SdkRepositoryFactory {
override fun getRepositories(userId: String?): Repositories =
Repositories(
@@ -34,12 +38,25 @@ class SdkRepositoryFactoryImpl(
override fun getClientManagedTokens(
userId: String?,
accessToken: String?,
): ClientManagedTokens =
SdkTokenRepository(
userId = userId,
accessToken = accessToken,
authDiskSource = authDiskSource,
)
override fun getClientSettings(): ClientSettings =
ClientSettings(
identityUrl = serviceClientConfig.baseUrlsProvider.getBaseIdentityUrl(),
apiUrl = serviceClientConfig.baseUrlsProvider.getBaseApiUrl(),
userAgent = serviceClientConfig.clientData.userAgent,
deviceType = DeviceType.ANDROID,
deviceIdentifier = serviceClientConfig.appIdProvider.uniqueAppId,
bitwardenClientVersion = serviceClientConfig.clientData.clientVersion,
bitwardenPackageType = null,
)
override fun getServerCommunicationConfigRepository(): ServerCommunicationConfigRepository =
ServerCommunicationConfigRepositoryImpl(
cookieDiskSource = cookieDiskSource,
@@ -11,10 +11,9 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
*/
class SdkTokenRepository(
private val userId: String?,
private val accessToken: String?,
private val authDiskSource: AuthDiskSource,
) : ClientManagedTokens {
override suspend fun getAccessToken(): String? =
userId?.let {
authDiskSource.getAccountTokens(userId = it)?.accessToken
}
accessToken ?: userId?.let { authDiskSource.getAccountTokens(userId = it)?.accessToken }
}
@@ -21,36 +21,36 @@ class GeneratorSdkSourceImpl(
override suspend fun generatePassword(
request: PasswordGeneratorRequest,
): Result<String> = runCatchingWithLogs {
getClient().generators().password(request)
useClient { generators().password(request) }
}
override suspend fun generatePassphrase(
request: PassphraseGeneratorRequest,
): Result<String> = runCatchingWithLogs {
getClient().generators().passphrase(request)
useClient { generators().passphrase(request) }
}
override suspend fun generatePlusAddressedEmail(
request: UsernameGeneratorRequest.Subaddress,
): Result<String> = runCatchingWithLogs {
getClient().generators().username(request)
useClient { generators().username(request) }
}
override suspend fun generateCatchAllEmail(
request: UsernameGeneratorRequest.Catchall,
): Result<String> = runCatchingWithLogs {
getClient().generators().username(request)
useClient { generators().username(request) }
}
override suspend fun generateRandomWord(
request: UsernameGeneratorRequest.Word,
): Result<String> = runCatchingWithLogs {
getClient().generators().username(request)
useClient { generators().username(request) }
}
override suspend fun generateForwardedServiceEmail(
request: UsernameGeneratorRequest.Forwarded,
): Result<String> = runCatchingWithLogs {
getClient().generators().username(request)
useClient { generators().username(request) }
}
}
@@ -20,6 +20,7 @@ import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import io.mockk.slot
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@@ -34,7 +35,7 @@ class AuthSdkSourceTest {
every { platform() } returns clientPlatform
}
private val sdkClientManager = mockk<SdkClientManager> {
coEvery { getOrCreateClient(userId = null) } returns client
coEvery { getOrCreateClient(userId = any()) } returns client
}
private val authSkdSource: AuthSdkSource = AuthSdkSourceImpl(
@@ -43,6 +44,10 @@ class AuthSdkSourceTest {
@Test
fun `getNewAuthRequest should call SDK and return a Result with correct data`() = runBlocking {
val slot = slot<suspend Client.() -> AuthRequestResponse>()
coEvery {
sdkClientManager.singleUseClient(block = capture(slot))
} coAnswers { slot.captured(client) }
val email = "test@gmail.com"
val expectedResult = mockk<AuthRequestResponse>()
coEvery {
@@ -62,6 +67,10 @@ class AuthSdkSourceTest {
@Test
fun `getUserFingerprint should call SDK and return a Result with correct data`() = runBlocking {
val slot = slot<suspend Client.() -> String>()
coEvery {
sdkClientManager.singleUseClient(block = capture(slot))
} coAnswers { slot.captured(client) }
val email = "email@gmail.com"
val publicKey = "publicKey"
val expectedResult = "fingerprint"
@@ -91,6 +100,10 @@ class AuthSdkSourceTest {
@Test
fun `hashPassword should call SDK and return a Result with the correct data`() = runBlocking {
val slot = slot<suspend Client.() -> String>()
coEvery {
sdkClientManager.singleUseClient(block = capture(slot))
} coAnswers { slot.captured(client) }
val email = "email"
val password = "password"
val kdf = mockk<Kdf>()
@@ -128,6 +141,10 @@ class AuthSdkSourceTest {
@Test
fun `makeKeyConnectorKeys should call SDK and return a Result with the correct data`() =
runBlocking {
val slot = slot<suspend Client.() -> KeyConnectorResponse>()
coEvery {
sdkClientManager.singleUseClient(block = capture(slot))
} coAnswers { slot.captured(client) }
val expectedResult = mockk<KeyConnectorResponse>()
coEvery { clientAuth.makeKeyConnectorKeys() } returns expectedResult
@@ -142,6 +159,10 @@ class AuthSdkSourceTest {
@Test
fun `makeRegisterKeys should call SDK and return a Result with the correct data`() =
runBlocking {
val slot = slot<suspend Client.() -> RegisterKeyResponse>()
coEvery {
sdkClientManager.singleUseClient(block = capture(slot))
} coAnswers { slot.captured(client) }
val email = "email"
val password = "password"
val kdf = mockk<Kdf>()
@@ -181,7 +202,6 @@ class AuthSdkSourceTest {
val orgPublicKey = "orgPublicKey"
val rememberDevice = true
val expectedResult = mockk<RegisterTdeKeyResponse>()
coEvery { sdkClientManager.getOrCreateClient(userId = userId) } returns client
coEvery {
clientAuth.makeRegisterTdeKeys(
email = email,
@@ -209,6 +229,10 @@ class AuthSdkSourceTest {
@Test
fun `passwordStrength should call SDK and return a Result with the correct data`() =
runBlocking {
val slot = slot<suspend Client.() -> PasswordStrength>()
coEvery {
sdkClientManager.singleUseClient(block = capture(slot))
} coAnswers { slot.captured(client) }
val email = "email"
val password = "password"
val additionalInputs = listOf("test1", "test2")
@@ -243,6 +267,10 @@ class AuthSdkSourceTest {
@Test
fun `satisfiesPolicy should call SDK and return a Result with the correct data`() =
runBlocking {
val slot = slot<suspend Client.() -> Boolean>()
coEvery {
sdkClientManager.singleUseClient(block = capture(slot))
} coAnswers { slot.captured(client) }
val password = "password"
val passwordStrength = PasswordStrength.LEVEL_3
val rawStrength = 3.toUByte()
@@ -72,6 +72,17 @@ class SdkClientManagerTest {
assertNotEquals(firstClient, thirdClient)
}
@Test
fun `singleUseClient should create a new client everytime and run the lambda with it`() =
runTest {
val sdkClientManager = createSdkClientManager()
val firstClient = sdkClientManager.singleUseClient { this }
// Additional calls should always create a new client
val secondClient = sdkClientManager.singleUseClient { this }
assertNotEquals(firstClient, secondClient)
}
@Test
fun `destroyClient should call close on the Client and remove it from the cache`() = runTest {
val sdkClientManager = createSdkClientManager()
@@ -88,7 +99,7 @@ class SdkClientManagerTest {
}
private fun createSdkClientManager(): SdkClientManagerImpl = SdkClientManagerImpl(
clientProvider = { mockk(relaxed = true) },
clientProvider = { _, _ -> mockk(relaxed = true) },
nativeLibraryManager = mockNativeLibraryManager,
featureFlagManager = mockk(),
sdkRepoFactory = sdkRepoFactory,
@@ -1,12 +1,21 @@
package com.x8bit.bitwarden.data.platform.manager.sdk
import com.bitwarden.core.ClientSettings
import com.bitwarden.core.DeviceType
import com.bitwarden.data.datasource.disk.ConfigDiskSource
import com.bitwarden.network.interceptor.BaseUrlsProvider
import com.bitwarden.network.model.BitwardenServiceClientConfig
import com.bitwarden.network.provider.AppIdProvider
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSource
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
import io.mockk.mockk
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Test
import java.time.Clock
import java.time.Instant
import java.time.ZoneOffset
class SdkRepositoryFactoryTests {
@@ -14,12 +23,32 @@ class SdkRepositoryFactoryTests {
private val cookieDiskSource: CookieDiskSource = mockk()
private val configDiskSource: ConfigDiskSource = mockk()
private val authDiskSource: AuthDiskSource = mockk()
private val serviceClientConfig: BitwardenServiceClientConfig = BitwardenServiceClientConfig(
clientData = BitwardenServiceClientConfig.ClientData(
userAgent = USER_AGENT,
clientName = CLIENT_NAME,
clientVersion = CLIENT_VERSION,
),
appIdProvider = object : AppIdProvider {
override val uniqueAppId: String get() = UNIQUE_APP_ID
},
baseUrlsProvider = object : BaseUrlsProvider {
override fun getBaseApiUrl(): String = BASE_API_URL
override fun getBaseIdentityUrl(): String = BASE_IDENTITY_URL
override fun getBaseEventsUrl(): String = BASE_EVENTS_URL
},
authTokenProvider = mockk(),
certificateProvider = mockk(),
cookieProvider = mockk(),
clock = FIXED_CLOCK,
)
private val sdkRepoFactory: SdkRepositoryFactory = SdkRepositoryFactoryImpl(
vaultDiskSource = vaultDiskSource,
cookieDiskSource = cookieDiskSource,
configDiskSource = configDiskSource,
authDiskSource = authDiskSource,
serviceClientConfig = serviceClientConfig,
)
@Test
@@ -45,18 +74,43 @@ class SdkRepositoryFactoryTests {
@Test
fun `getClientManagedTokens should create a new client`() {
val userId = "userId"
val firstClient = sdkRepoFactory.getClientManagedTokens(userId = userId)
val firstClient = sdkRepoFactory.getClientManagedTokens(
userId = userId,
accessToken = null,
)
// Additional calls for the same userId should create a repo
val secondClient = sdkRepoFactory.getClientManagedTokens(userId = userId)
val secondClient = sdkRepoFactory.getClientManagedTokens(
userId = userId,
accessToken = null,
)
assertNotEquals(firstClient, secondClient)
// Additional calls for different userIds should return a different repo
val otherUserId = "otherUserId"
val thirdClient = sdkRepoFactory.getClientManagedTokens(userId = otherUserId)
val thirdClient = sdkRepoFactory.getClientManagedTokens(
userId = otherUserId,
accessToken = null,
)
assertNotEquals(firstClient, thirdClient)
}
@Test
fun `getClientSettings should create correct getClientSettings`() {
assertEquals(
ClientSettings(
identityUrl = BASE_IDENTITY_URL,
apiUrl = BASE_API_URL,
userAgent = USER_AGENT,
deviceType = DeviceType.ANDROID,
deviceIdentifier = UNIQUE_APP_ID,
bitwardenClientVersion = CLIENT_VERSION,
bitwardenPackageType = null,
),
sdkRepoFactory.getClientSettings(),
)
}
@Test
fun `getServerCommunicationConfigRepository should create a new repository`() {
val firstRepo = sdkRepoFactory.getServerCommunicationConfigRepository()
@@ -64,3 +118,16 @@ class SdkRepositoryFactoryTests {
assertNotEquals(firstRepo, secondRepo)
}
}
private val FIXED_CLOCK: Clock = Clock.fixed(
Instant.parse("2023-10-27T12:00:00Z"),
ZoneOffset.UTC,
)
private const val BASE_API_URL: String = "https://api.bitwarden.com"
private const val BASE_EVENTS_URL: String = "https://events.bitwarden.com"
private const val BASE_IDENTITY_URL: String = "https://identity.bitwarden.com"
private const val CLIENT_NAME: String = "mobile"
private const val CLIENT_VERSION: String = "2026.4.1"
private const val UNIQUE_APP_ID: String = "app_id_12345"
private const val USER_AGENT: String = "user (agent)"
@@ -34,7 +34,6 @@ class SdkTokenRepositoryTest {
}
}
@Suppress("MaxLineLength")
@Test
fun `getAccessToken should return null when userId is valid and accessToken is null`() =
runTest {
@@ -69,10 +68,23 @@ class SdkTokenRepositoryTest {
}
}
@Test
fun `getAccessToken should return access token when accessToken is manually provided`() =
runTest {
val accessToken = "access_token"
val repository = createSdkTokenRepository(accessToken = accessToken)
assertEquals(accessToken, repository.getAccessToken())
verify(exactly = 0) {
authDiskSource.getAccountTokens(userId = USER_ID)
}
}
private fun createSdkTokenRepository(
userId: String? = USER_ID,
accessToken: String? = null,
): SdkTokenRepository = SdkTokenRepository(
userId = userId,
accessToken = accessToken,
authDiskSource = authDiskSource,
)
}
@@ -13,6 +13,7 @@ import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
@@ -23,7 +24,8 @@ class GeneratorSdkSourceTest {
every { generators() } returns clientGenerators
}
private val sdkClientManager = mockk<SdkClientManager> {
coEvery { getOrCreateClient(userId = null) } returns client
val slot = slot<suspend Client.() -> String>()
coEvery { singleUseClient(block = capture(slot)) } coAnswers { slot.captured(client) }
}
private val generatorSdkSource: GeneratorSdkSource = GeneratorSdkSourceImpl(sdkClientManager)
@@ -42,11 +42,11 @@ object PlatformNetworkModule {
@Provides
@Singleton
fun provideBitwardenServiceClient(
fun provideBitwardenServiceClientConfig(
baseUrlsProvider: BaseUrlsProvider,
authDiskSource: AuthDiskSource,
clock: Clock,
): BitwardenServiceClient = bitwardenServiceClient(
): BitwardenServiceClientConfig =
BitwardenServiceClientConfig(
clock = clock,
appIdProvider = authDiskSource,
@@ -59,6 +59,7 @@ object PlatformNetworkModule {
enableHttpBodyLogging = BuildConfig.DEBUG,
authTokenProvider = object : AuthTokenProvider {
override fun getAuthTokenDataOrNull(): AuthTokenData? = null
override fun getAuthTokenDataOrNull(userId: String): AuthTokenData? = null
},
certificateProvider = object : CertificateProvider {
@@ -66,7 +67,7 @@ object PlatformNetworkModule {
keyType: Array<out String>?,
issuers: Array<out Principal>?,
socket: Socket?,
) = ""
): String = ""
override fun getCertificateChain(alias: String?): Array<X509Certificate>? = null
@@ -77,9 +78,16 @@ object PlatformNetworkModule {
override fun getCookies(hostname: String): List<NetworkCookie> = emptyList()
override fun acquireCookies(hostname: String) = Unit
override fun acquireCookies(hostname: String): Unit = Unit
},
),
)
@Provides
@Singleton
fun provideBitwardenServiceClient(
serviceClientConfig: BitwardenServiceClientConfig,
): BitwardenServiceClient = bitwardenServiceClient(
config = serviceClientConfig,
)
@Provides
@@ -18,7 +18,7 @@ data class BitwardenServiceClientConfig(
val authTokenProvider: AuthTokenProvider,
val certificateProvider: CertificateProvider,
val cookieProvider: CookieProvider,
val clock: Clock = Clock.systemDefaultZone(),
val clock: Clock,
val enableHttpBodyLogging: Boolean = false,
) {
/**