mirror of
https://github.com/bitwarden/android.git
synced 2025-02-16 11:59:57 +03:00
[PM-9401] Server feature flags manager (#3656)
This commit is contained in:
parent
02167024b1
commit
994a577600
15 changed files with 459 additions and 57 deletions
|
@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.platform.datasource.network.model
|
|||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
/**
|
||||
* Represents the response model for configuration data fetched from the server.
|
||||
|
@ -31,7 +32,7 @@ data class ConfigResponseJson(
|
|||
val environment: EnvironmentJson?,
|
||||
|
||||
@SerialName("featureStates")
|
||||
val featureStates: Map<String, Boolean>?,
|
||||
val featureStates: Map<String, JsonPrimitive>?,
|
||||
) {
|
||||
/**
|
||||
* Represents a server in the configuration response.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Manages the available feature flags for the Bitwarden application.
|
||||
*/
|
||||
interface FeatureFlagManager {
|
||||
/**
|
||||
* Returns a map of constant feature flags that are only used locally.
|
||||
*/
|
||||
val sdkFeatureFlags: Map<String, Boolean>
|
||||
|
||||
/**
|
||||
* Returns a flow emitting the value of flag [key] which is of generic type [T].
|
||||
* If the value of the flag cannot be retrieved, the default value of [key] will be returned
|
||||
*/
|
||||
fun <T : Any> getFeatureFlagFlow(key: FlagKey<T>): Flow<T>
|
||||
|
||||
/**
|
||||
* Get value for feature flag with [key] and returns it as generic type [T].
|
||||
* If no value is found the the given [key] its default value will be returned.
|
||||
* Cached flags can be invalidated with [forceRefresh]
|
||||
*/
|
||||
suspend fun <T : Any> getFeatureFlag(
|
||||
key: FlagKey<T>,
|
||||
forceRefresh: Boolean,
|
||||
): T
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private const val CIPHER_KEY_ENCRYPTION_KEY = "enableCipherKeyEncryption"
|
||||
|
||||
/**
|
||||
* Primary implementation of [FeatureFlagManager].
|
||||
*/
|
||||
class FeatureFlagManagerImpl(
|
||||
private val serverConfigRepository: ServerConfigRepository,
|
||||
) : FeatureFlagManager {
|
||||
|
||||
override val sdkFeatureFlags: Map<String, Boolean>
|
||||
get() = mapOf(CIPHER_KEY_ENCRYPTION_KEY to true)
|
||||
|
||||
override fun <T : Any> getFeatureFlagFlow(key: FlagKey<T>): Flow<T> =
|
||||
serverConfigRepository
|
||||
.serverConfigStateFlow
|
||||
.map { serverConfig ->
|
||||
serverConfig.getFlagValueOrDefault(key = key)
|
||||
}
|
||||
|
||||
override suspend fun <T : Any> getFeatureFlag(
|
||||
key: FlagKey<T>,
|
||||
forceRefresh: Boolean,
|
||||
): T =
|
||||
serverConfigRepository
|
||||
.getServerConfig(forceRefresh = forceRefresh)
|
||||
.getFlagValueOrDefault(key = key)
|
||||
}
|
||||
|
||||
private fun <T : Any> ServerConfig?.getFlagValueOrDefault(key: FlagKey<T>): T {
|
||||
val defaultValue = key.defaultValue
|
||||
return this?.serverData
|
||||
?.featureStates
|
||||
?.get(key.keyName)
|
||||
?.let {
|
||||
try {
|
||||
// Suppressed since we are checking the type before doing the cast
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
when (defaultValue::class) {
|
||||
Boolean::class -> it.content.toBoolean() as T
|
||||
String::class -> it.content as T
|
||||
Int::class -> it.content.toInt() as T
|
||||
else -> defaultValue
|
||||
}
|
||||
} catch (ex: ClassCastException) {
|
||||
defaultValue
|
||||
} catch (ex: NumberFormatException) {
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
?: defaultValue
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
|
||||
/**
|
||||
* Primary implementation of [SdkClientManager].
|
||||
*/
|
||||
class SdkClientManagerImpl(
|
||||
private val featureFlagManager: BitwardenFeatureFlagManager,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
private val clientProvider: suspend () -> Client = {
|
||||
Client(settings = null).apply {
|
||||
platform().loadFlags(featureFlagManager.featureFlags)
|
||||
platform().loadFlags(featureFlagManager.sdkFeatureFlags)
|
||||
}
|
||||
},
|
||||
) : SdkClientManager {
|
||||
|
|
|
@ -22,6 +22,8 @@ import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
|
|||
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.CrashLogsManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.CrashLogsManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.NetworkConnectionManager
|
||||
|
@ -47,8 +49,8 @@ import com.x8bit.bitwarden.data.platform.manager.garbage.GarbageCollectionManage
|
|||
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
@ -136,10 +138,19 @@ object PlatformManagerModule {
|
|||
dispatcherManager = dispatcherManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesFeatureFlagManager(
|
||||
serverConfigRepository: ServerConfigRepository,
|
||||
): FeatureFlagManager =
|
||||
FeatureFlagManagerImpl(
|
||||
serverConfigRepository = serverConfigRepository,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSdkClientManager(
|
||||
featureFlagManager: BitwardenFeatureFlagManager,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
): SdkClientManager = SdkClientManagerImpl(
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
/**
|
||||
* Class to hold feature flag keys.
|
||||
* @property [keyName] corresponds to the string value of a given key
|
||||
* @property [defaultValue] corresponds to default value of the flag of type [T]
|
||||
*/
|
||||
sealed class FlagKey<out T : Any> {
|
||||
abstract val keyName: String
|
||||
abstract val defaultValue: T
|
||||
|
||||
/**
|
||||
* Data object holding the key for Email Verification feature
|
||||
*/
|
||||
data object EmailVerification : FlagKey<Boolean>() {
|
||||
override val keyName: String = "email-verification"
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the key for an Int flag to be used in tests
|
||||
*/
|
||||
data object DummyInt : FlagKey<Int>() {
|
||||
override val keyName: String = "dummy-int"
|
||||
override val defaultValue: Int = Int.MIN_VALUE
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the key for an String flag to be used in tests
|
||||
*/
|
||||
data object DummyString : FlagKey<String>() {
|
||||
override val keyName: String = "dummy-string"
|
||||
override val defaultValue: String = "defaultValue"
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
/**
|
||||
* Manages the available feature flags for the Bitwarden application.
|
||||
*/
|
||||
interface BitwardenFeatureFlagManager {
|
||||
/**
|
||||
* Returns a map of feature flags.
|
||||
*/
|
||||
val featureFlags: Map<String, Boolean>
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
private const val CIPHER_KEY_ENCRYPTION_KEY = "enableCipherKeyEncryption"
|
||||
|
||||
/**
|
||||
* Primary implementation of [BitwardenFeatureFlagManager].
|
||||
*/
|
||||
class BitwardenFeatureFlagManagerImpl : BitwardenFeatureFlagManager {
|
||||
override val featureFlags: Map<String, Boolean>
|
||||
get() = mapOf(CIPHER_KEY_ENCRYPTION_KEY to true)
|
||||
}
|
|
@ -4,8 +4,6 @@ import com.bitwarden.sdk.Fido2CredentialStore
|
|||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManagerImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSourceImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.Fido2CredentialStoreImpl
|
||||
|
@ -34,11 +32,6 @@ object VaultSdkModule {
|
|||
dispatcherManager = dispatcherManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesBitwardenFeatureFlagManager(): BitwardenFeatureFlagManager =
|
||||
BitwardenFeatureFlagManagerImpl()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesFido2CredentialStore(
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponse
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.EnvironmentJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.ServerJson
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Test
|
||||
|
@ -106,6 +107,9 @@ private val SERVER_CONFIG = ServerConfig(
|
|||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf("duo-redirect" to true, "flexible-collections-v-1" to false),
|
||||
featureStates = mapOf(
|
||||
"duo-redirect" to JsonPrimitive(true),
|
||||
"flexible-collections-v-1" to JsonPrimitive(false),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.x8bit.bitwarden.data.platform.datasource.network.api.ConfigApi
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
@ -61,6 +62,6 @@ private val CONFIG_RESPONSE = ConfigResponseJson(
|
|||
ssoUrl = "ssoUrl",
|
||||
),
|
||||
featureStates = mapOf(
|
||||
"feature one" to false,
|
||||
"feature one" to JsonPrimitive(false),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.EnvironmentJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.ServerJson
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.FakeServerConfigRepository
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import java.time.Instant
|
||||
|
||||
class FeatureFlagManagerTest {
|
||||
private val fakeServerConfigRepository = FakeServerConfigRepository()
|
||||
|
||||
private var manager = FeatureFlagManagerImpl(
|
||||
serverConfigRepository = fakeServerConfigRepository,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `sdkFeatureFlags should return set feature flags`() {
|
||||
val expected = mapOf("enableCipherKeyEncryption" to true)
|
||||
|
||||
val actual = manager.sdkFeatureFlags
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ServerConfigRepository flow with value should trigger new flags`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = null
|
||||
assertNull(
|
||||
fakeServerConfigRepository.serverConfigValue,
|
||||
)
|
||||
|
||||
// This should trigger a new server config to be fetched
|
||||
fakeServerConfigRepository.serverConfigValue = SERVER_CONFIG
|
||||
|
||||
manager.getFeatureFlagFlow(FlagKey.EmailVerification).test {
|
||||
assertNotNull(
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ServerConfigRepository flow with null should trigger default flag value value`() =
|
||||
runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = null
|
||||
|
||||
manager.getFeatureFlagFlow(FlagKey.EmailVerification).test {
|
||||
assertFalse(
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag Boolean should return value if exists`() = runTest {
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.EmailVerification,
|
||||
forceRefresh = true,
|
||||
)
|
||||
assertTrue(flagValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag Boolean should return default value if doesn't exists`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = SERVER_CONFIG.copy(
|
||||
serverData = SERVER_CONFIG
|
||||
.serverData
|
||||
.copy(
|
||||
featureStates = mapOf("flag-example" to JsonPrimitive(123)),
|
||||
),
|
||||
)
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.EmailVerification,
|
||||
forceRefresh = false,
|
||||
)
|
||||
assertFalse(flagValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag Int should return value if exists`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = SERVER_CONFIG.copy(
|
||||
serverData = SERVER_CONFIG
|
||||
.serverData
|
||||
.copy(
|
||||
featureStates = mapOf("dummy-int" to JsonPrimitive(123)),
|
||||
),
|
||||
)
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.DummyInt,
|
||||
forceRefresh = false,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
123,
|
||||
flagValue,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag Int should return default value if doesn't exists`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = SERVER_CONFIG.copy(
|
||||
serverData = SERVER_CONFIG
|
||||
.serverData
|
||||
.copy(
|
||||
featureStates = mapOf("flag-example" to JsonPrimitive(123)),
|
||||
),
|
||||
)
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.DummyInt,
|
||||
forceRefresh = false,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
Int.MIN_VALUE,
|
||||
flagValue,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag String should return value if exists`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = SERVER_CONFIG.copy(
|
||||
serverData = SERVER_CONFIG
|
||||
.serverData
|
||||
.copy(
|
||||
featureStates = mapOf("dummy-string" to JsonPrimitive("niceValue")),
|
||||
),
|
||||
)
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.DummyString,
|
||||
forceRefresh = false,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"niceValue",
|
||||
flagValue,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag String should return default value if doesn't exists`() =
|
||||
runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = SERVER_CONFIG.copy(
|
||||
serverData = SERVER_CONFIG
|
||||
.serverData
|
||||
.copy(
|
||||
featureStates = mapOf("flag-example" to JsonPrimitive("niceValue")),
|
||||
),
|
||||
)
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.DummyString,
|
||||
forceRefresh = false,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"defaultValue",
|
||||
flagValue,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag Boolean should return default value if no flags available`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = null
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.EmailVerification,
|
||||
forceRefresh = false,
|
||||
)
|
||||
|
||||
assertFalse(
|
||||
flagValue,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag Int should return default value if no flags available`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = null
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.DummyInt,
|
||||
forceRefresh = false,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
Int.MIN_VALUE,
|
||||
flagValue,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getFeatureFlag String should return default value if no flags available`() = runTest {
|
||||
fakeServerConfigRepository.serverConfigValue = null
|
||||
|
||||
val flagValue = manager.getFeatureFlag(
|
||||
key = FlagKey.DummyString,
|
||||
forceRefresh = false,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"defaultValue",
|
||||
flagValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val SERVER_CONFIG = ServerConfig(
|
||||
lastSync = Instant.parse("2023-10-27T12:00:00Z").toEpochMilli(),
|
||||
serverData = ConfigResponseJson(
|
||||
type = null,
|
||||
version = "2024.7.0",
|
||||
gitHash = "25cf6119-dirty",
|
||||
server = ServerJson(
|
||||
name = "example",
|
||||
url = "https://localhost:8080",
|
||||
),
|
||||
environment = EnvironmentJson(
|
||||
cloudRegion = null,
|
||||
vaultUrl = "https://localhost:8080",
|
||||
apiUrl = "http://localhost:4000",
|
||||
identityUrl = "http://localhost:33656",
|
||||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf(
|
||||
"email-verification" to JsonPrimitive(true),
|
||||
"flexible-collections-v-1" to JsonPrimitive(false),
|
||||
),
|
||||
),
|
||||
)
|
|
@ -15,6 +15,7 @@ import com.x8bit.bitwarden.data.platform.util.asSuccess
|
|||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
|
@ -162,7 +163,10 @@ private val SERVER_CONFIG = ServerConfig(
|
|||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf("duo-redirect" to true, "flexible-collections-v-1" to false),
|
||||
featureStates = mapOf(
|
||||
"duo-redirect" to JsonPrimitive(true),
|
||||
"flexible-collections-v-1" to JsonPrimitive(false),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -182,5 +186,8 @@ private val CONFIG_RESPONSE_JSON = ConfigResponseJson(
|
|||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf("duo-redirect" to true, "flexible-collections-v-1" to false),
|
||||
featureStates = mapOf(
|
||||
"duo-redirect" to JsonPrimitive(true),
|
||||
"flexible-collections-v-1" to JsonPrimitive(false),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package com.x8bit.bitwarden.data.platform.repository.util
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.EnvironmentJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.ServerJson
|
||||
import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepository
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import java.time.Instant
|
||||
|
||||
class FakeServerConfigRepository : ServerConfigRepository {
|
||||
var serverConfigValue: ServerConfig?
|
||||
get() = mutableServerConfigFlow.value
|
||||
set(value) {
|
||||
mutableServerConfigFlow.value = value
|
||||
}
|
||||
|
||||
private val mutableServerConfigFlow = MutableStateFlow<ServerConfig?>(SERVER_CONFIG)
|
||||
|
||||
override suspend fun getServerConfig(forceRefresh: Boolean): ServerConfig? {
|
||||
if (forceRefresh) {
|
||||
return SERVER_CONFIG
|
||||
}
|
||||
|
||||
return serverConfigValue
|
||||
}
|
||||
|
||||
override val serverConfigStateFlow: StateFlow<ServerConfig?>
|
||||
get() = mutableServerConfigFlow
|
||||
}
|
||||
|
||||
private val SERVER_CONFIG = ServerConfig(
|
||||
lastSync = Instant.parse("2023-10-27T12:00:00Z").toEpochMilli(),
|
||||
serverData = ConfigResponseJson(
|
||||
type = null,
|
||||
version = "2024.7.0",
|
||||
gitHash = "25cf6119-dirty",
|
||||
server = ServerJson(
|
||||
name = "example",
|
||||
url = "https://localhost:8080",
|
||||
),
|
||||
environment = EnvironmentJson(
|
||||
cloudRegion = null,
|
||||
vaultUrl = "https://localhost:8080",
|
||||
apiUrl = "http://localhost:4000",
|
||||
identityUrl = "http://localhost:33656",
|
||||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf(
|
||||
"duo-redirect" to JsonPrimitive(true),
|
||||
"flexible-collections-v-1" to JsonPrimitive(false),
|
||||
"email-verification" to JsonPrimitive(true),
|
||||
),
|
||||
),
|
||||
)
|
|
@ -1,18 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
class BitwardenFeatureFlagManagerTest {
|
||||
|
||||
private val bitwardenFeatureFlagManager = BitwardenFeatureFlagManagerImpl()
|
||||
|
||||
@Test
|
||||
fun `featureFlags should return set feature flags`() {
|
||||
val expected = mapOf("enableCipherKeyEncryption" to true)
|
||||
|
||||
val actual = bitwardenFeatureFlagManager.featureFlags
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue