generated from bitwarden/template
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BWA-59] Define feature flag manager (#177)
- Loading branch information
1 parent
0d77e70
commit b49d8a1
Showing
5 changed files
with
192 additions
and
0 deletions.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
app/src/main/kotlin/com/bitwarden/authenticator/data/platform/manager/FeatureFlagManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.bitwarden.authenticator.data.platform.manager | ||
|
||
import com.bitwarden.authenticator.data.platform.manager.model.FeatureFlag | ||
import kotlinx.coroutines.flow.Flow | ||
|
||
/** | ||
* Manages the available feature flags for the Bitwarden application. | ||
*/ | ||
interface FeatureFlagManager { | ||
|
||
/** | ||
* 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: FeatureFlag<T>): Flow<T> | ||
|
||
/** | ||
* Gets the value for feature flag with [key] and returns it as generic type [T]. | ||
* If no value is found the given [key] its [FeatureFlag.defaultValue] will be returned. | ||
*/ | ||
fun <T : Any> getFeatureFlag( | ||
key: FeatureFlag<T>, | ||
): T | ||
} |
52 changes: 52 additions & 0 deletions
52
...c/main/kotlin/com/bitwarden/authenticator/data/platform/manager/FeatureFlagManagerImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.bitwarden.authenticator.data.platform.manager | ||
|
||
import com.bitwarden.authenticator.data.platform.datasource.disk.model.FeatureFlagsConfiguration | ||
import com.bitwarden.authenticator.data.platform.manager.model.FeatureFlag | ||
import com.bitwarden.authenticator.data.platform.repository.FeatureFlagRepository | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.map | ||
|
||
/** | ||
* Primary implementation of [FeatureFlagManager]. | ||
*/ | ||
class FeatureFlagManagerImpl( | ||
private val featureFlagRepository: FeatureFlagRepository, | ||
) : FeatureFlagManager { | ||
|
||
override fun <T : Any> getFeatureFlagFlow(key: FeatureFlag<T>): Flow<T> = | ||
featureFlagRepository | ||
.featureFlagConfigStateFlow | ||
.map { serverConfig -> | ||
serverConfig.getFlagValueOrDefault(key = key) | ||
} | ||
|
||
override fun <T : Any> getFeatureFlag(key: FeatureFlag<T>): T = | ||
featureFlagRepository | ||
.featureFlagConfigStateFlow | ||
.value | ||
.getFlagValueOrDefault(key = key) | ||
} | ||
|
||
private fun <T : Any> FeatureFlagsConfiguration?.getFlagValueOrDefault(key: FeatureFlag<T>): T { | ||
val defaultValue = key.defaultValue | ||
return this | ||
?.featureFlags | ||
?.get(key.name) | ||
?.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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
...src/test/java/com/bitwarden/authenticator/data/platform/manager/FeatureFlagManagerTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package com.bitwarden.authenticator.data.platform.manager | ||
|
||
import app.cash.turbine.test | ||
import com.bitwarden.authenticator.data.platform.datasource.disk.model.FeatureFlagsConfiguration | ||
import com.bitwarden.authenticator.data.platform.manager.model.LocalFeatureFlag | ||
import com.bitwarden.authenticator.data.platform.repository.util.FakeFeatureFlagRepository | ||
import kotlinx.coroutines.test.runTest | ||
import kotlinx.serialization.json.JsonPrimitive | ||
import org.junit.Test | ||
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 | ||
|
||
class FeatureFlagManagerTest { | ||
private val fakeFeatureFlagRepository = FakeFeatureFlagRepository() | ||
|
||
private val featureFlagManager = FeatureFlagManagerImpl( | ||
featureFlagRepository = fakeFeatureFlagRepository, | ||
) | ||
|
||
@Test | ||
fun `FeatureFlagRepository flow with value should trigger new flags`() = runTest { | ||
fakeFeatureFlagRepository.featureFlagsConfiguration = null | ||
assertNull( | ||
fakeFeatureFlagRepository.featureFlagsConfiguration, | ||
) | ||
|
||
fakeFeatureFlagRepository.featureFlagsConfiguration = FEATURE_FLAGS_CONFIG | ||
|
||
featureFlagManager | ||
.getFeatureFlagFlow(LocalFeatureFlag.BitwardenAuthenticationEnabled) | ||
.test { | ||
assertNotNull(awaitItem()) | ||
} | ||
} | ||
|
||
@Test | ||
fun `getFeatureFlag should return value if exists`() = runTest { | ||
fakeFeatureFlagRepository.featureFlagsConfiguration = FEATURE_FLAGS_CONFIG | ||
|
||
val flagValue = featureFlagManager.getFeatureFlag( | ||
key = LocalFeatureFlag.BitwardenAuthenticationEnabled, | ||
) | ||
|
||
assertTrue(flagValue) | ||
} | ||
|
||
@Test | ||
fun `getFeatureFlag should return default value if flag doesn't exist`() { | ||
fakeFeatureFlagRepository.featureFlagsConfiguration = FEATURE_FLAGS_CONFIG.copy( | ||
featureFlags = emptyMap() | ||
) | ||
|
||
val flagValue = featureFlagManager.getFeatureFlag( | ||
LocalFeatureFlag.BitwardenAuthenticationEnabled, | ||
) | ||
|
||
assertFalse(flagValue) | ||
} | ||
} | ||
|
||
private val FEATURE_FLAGS_CONFIG = | ||
FeatureFlagsConfiguration( | ||
mapOf( | ||
LocalFeatureFlag.BitwardenAuthenticationEnabled.name to | ||
JsonPrimitive(true), | ||
) | ||
) |
38 changes: 38 additions & 0 deletions
38
...va/com/bitwarden/authenticator/data/platform/repository/util/FakeFeatureFlagRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.bitwarden.authenticator.data.platform.repository.util | ||
|
||
import com.bitwarden.authenticator.data.platform.datasource.disk.model.FeatureFlagsConfiguration | ||
import com.bitwarden.authenticator.data.platform.manager.model.LocalFeatureFlag | ||
import com.bitwarden.authenticator.data.platform.repository.FeatureFlagRepository | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.serialization.json.JsonPrimitive | ||
|
||
/** | ||
* Faked implementation of [FeatureFlagRepository] for testing. | ||
*/ | ||
class FakeFeatureFlagRepository : FeatureFlagRepository { | ||
var featureFlagsConfiguration: FeatureFlagsConfiguration? | ||
get() = mutableFeatureFlagsConfiguration.value | ||
set(value) { | ||
mutableFeatureFlagsConfiguration.value = value | ||
} | ||
|
||
private val mutableFeatureFlagsConfiguration = | ||
MutableStateFlow<FeatureFlagsConfiguration?>(FEATURE_FLAGS_CONFIG) | ||
|
||
override val featureFlagConfigStateFlow: StateFlow<FeatureFlagsConfiguration?> = | ||
mutableFeatureFlagsConfiguration | ||
|
||
override suspend fun getFeatureFlagsConfiguration(): FeatureFlagsConfiguration { | ||
return featureFlagsConfiguration | ||
?: FEATURE_FLAGS_CONFIG | ||
} | ||
} | ||
|
||
private val FEATURE_FLAGS_CONFIG = | ||
FeatureFlagsConfiguration( | ||
featureFlags = mapOf( | ||
LocalFeatureFlag.BitwardenAuthenticationEnabled.name to | ||
JsonPrimitive(LocalFeatureFlag.BitwardenAuthenticationEnabled.defaultValue) | ||
) | ||
) |