Rework the feature flag module.
Fix typo, rename class and interface, add doc, do small refacto, to improve code clarity.
This commit is contained in:
parent
4a870fc4a5
commit
04b9d3cc2c
8 changed files with 34 additions and 25 deletions
|
|
@ -28,7 +28,8 @@ interface FeatureFlagService {
|
||||||
* @param feature the feature to enable or disable
|
* @param feature the feature to enable or disable
|
||||||
* @param enabled true to enable the feature
|
* @param enabled true to enable the feature
|
||||||
*
|
*
|
||||||
* @return true if the method succeeds, ie if a RuntimeFeatureFlagProvider is registered
|
* @return true if the method succeeds, ie if a [io.element.android.libraries.featureflag.impl.MutableFeatureFlagProvider]
|
||||||
|
* is registered
|
||||||
*/
|
*/
|
||||||
suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean
|
suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class DefaultFeatureFlagService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean {
|
override suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean {
|
||||||
return providers.filterIsInstance(RuntimeFeatureFlagProvider::class.java)
|
return providers.filterIsInstance(MutableFeatureFlagProvider::class.java)
|
||||||
.sortedBy(FeatureFlagProvider::priority)
|
.sortedBy(FeatureFlagProvider::priority)
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.setFeatureEnabled(feature, enabled)
|
?.setFeatureEnabled(feature, enabled)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,6 @@ package io.element.android.libraries.featureflag.impl
|
||||||
|
|
||||||
import io.element.android.libraries.featureflag.api.Feature
|
import io.element.android.libraries.featureflag.api.Feature
|
||||||
|
|
||||||
interface RuntimeFeatureFlagProvider : FeatureFlagProvider {
|
interface MutableFeatureFlagProvider : FeatureFlagProvider {
|
||||||
suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean)
|
suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean)
|
||||||
}
|
}
|
||||||
|
|
@ -30,12 +30,13 @@ import javax.inject.Inject
|
||||||
|
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_featureflag")
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_featureflag")
|
||||||
|
|
||||||
class PreferencesFeatureFlagProvider @Inject constructor(@ApplicationContext context: Context) : RuntimeFeatureFlagProvider {
|
/**
|
||||||
|
* Note: this will be used only in the nightly and in the debug build.
|
||||||
|
*/
|
||||||
|
class PreferencesFeatureFlagProvider @Inject constructor(@ApplicationContext context: Context) : MutableFeatureFlagProvider {
|
||||||
private val store = context.dataStore
|
private val store = context.dataStore
|
||||||
|
|
||||||
override val priority: Int
|
override val priority = MEDIUM_PRIORITY
|
||||||
get() = MEDIUM_PRIORITY
|
|
||||||
|
|
||||||
override suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean) {
|
override suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean) {
|
||||||
store.edit { prefs ->
|
store.edit { prefs ->
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,14 @@ import io.element.android.libraries.featureflag.api.Feature
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class BuildtimeFeatureFlagProvider @Inject constructor() :
|
/**
|
||||||
|
* This provider is used for release build.
|
||||||
|
* Change the value return by [isFeatureEnabled] to enable/disable features.
|
||||||
|
*/
|
||||||
|
class StaticFeatureFlagProvider @Inject constructor() :
|
||||||
FeatureFlagProvider {
|
FeatureFlagProvider {
|
||||||
|
|
||||||
override val priority: Int
|
override val priority = LOW_PRIORITY
|
||||||
get() = LOW_PRIORITY
|
|
||||||
|
|
||||||
override suspend fun isFeatureEnabled(feature: Feature): Boolean {
|
override suspend fun isFeatureEnabled(feature: Feature): Boolean {
|
||||||
return if (feature is FeatureFlags) {
|
return if (feature is FeatureFlags) {
|
||||||
|
|
@ -22,7 +22,7 @@ import dagger.Provides
|
||||||
import dagger.multibindings.ElementsIntoSet
|
import dagger.multibindings.ElementsIntoSet
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
import io.element.android.libraries.core.meta.BuildType
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.featureflag.impl.BuildtimeFeatureFlagProvider
|
import io.element.android.libraries.featureflag.impl.StaticFeatureFlagProvider
|
||||||
import io.element.android.libraries.featureflag.impl.FeatureFlagProvider
|
import io.element.android.libraries.featureflag.impl.FeatureFlagProvider
|
||||||
import io.element.android.libraries.featureflag.impl.PreferencesFeatureFlagProvider
|
import io.element.android.libraries.featureflag.impl.PreferencesFeatureFlagProvider
|
||||||
|
|
||||||
|
|
@ -35,14 +35,18 @@ object FeatureFlagModule {
|
||||||
@ElementsIntoSet
|
@ElementsIntoSet
|
||||||
fun providesFeatureFlagProvider(
|
fun providesFeatureFlagProvider(
|
||||||
buildType: BuildType,
|
buildType: BuildType,
|
||||||
runtimeFeatureFlagProvider: PreferencesFeatureFlagProvider,
|
mutableFeatureFlagProvider: PreferencesFeatureFlagProvider,
|
||||||
buildtimeFeatureFlagProvider: BuildtimeFeatureFlagProvider,
|
staticFeatureFlagProvider: StaticFeatureFlagProvider,
|
||||||
): Set<FeatureFlagProvider> {
|
): Set<FeatureFlagProvider> {
|
||||||
val providers = HashSet<FeatureFlagProvider>()
|
val providers = HashSet<FeatureFlagProvider>()
|
||||||
if (buildType == BuildType.RELEASE) {
|
when (buildType) {
|
||||||
providers.add(buildtimeFeatureFlagProvider)
|
BuildType.RELEASE -> {
|
||||||
} else {
|
providers.add(staticFeatureFlagProvider)
|
||||||
providers.add(runtimeFeatureFlagProvider)
|
}
|
||||||
|
BuildType.NIGHTLY,
|
||||||
|
BuildType.DEBUG -> {
|
||||||
|
providers.add(mutableFeatureFlagProvider)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return providers
|
return providers
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class DefaultFeatureFlagServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given service with a runtime provider when set enabled feature is called then it returns true`() = runTest {
|
fun `given service with a runtime provider when set enabled feature is called then it returns true`() = runTest {
|
||||||
val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0)
|
val featureFlagProvider = FakeMutableFeatureFlagProvider(0)
|
||||||
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider))
|
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider))
|
||||||
val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
||||||
assertThat(result).isEqualTo(true)
|
assertThat(result).isEqualTo(true)
|
||||||
|
|
@ -47,7 +47,7 @@ class DefaultFeatureFlagServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given service with a runtime provider and feature enabled when feature is checked then it returns the correct value`() = runTest {
|
fun `given service with a runtime provider and feature enabled when feature is checked then it returns the correct value`() = runTest {
|
||||||
val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0)
|
val featureFlagProvider = FakeMutableFeatureFlagProvider(0)
|
||||||
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider))
|
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider))
|
||||||
featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
||||||
assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true)
|
assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true)
|
||||||
|
|
@ -57,11 +57,11 @@ class DefaultFeatureFlagServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given service with 2 runtime providers when feature is checked then it uses the priority correctly`() = runTest {
|
fun `given service with 2 runtime providers when feature is checked then it uses the priority correctly`() = runTest {
|
||||||
val lowPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(LOW_PRIORITY)
|
val lowPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(LOW_PRIORITY)
|
||||||
val highPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(HIGH_PRIORITY)
|
val highPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(HIGH_PRIORITY)
|
||||||
val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityfeatureFlagProvider, highPriorityfeatureFlagProvider))
|
val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityFeatureFlagProvider, highPriorityFeatureFlagProvider))
|
||||||
lowPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, false)
|
lowPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, false)
|
||||||
highPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
highPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
||||||
assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true)
|
assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.libraries.featureflag.impl
|
||||||
|
|
||||||
import io.element.android.libraries.featureflag.api.Feature
|
import io.element.android.libraries.featureflag.api.Feature
|
||||||
|
|
||||||
class FakeRuntimeFeatureFlagProvider(override val priority: Int) : RuntimeFeatureFlagProvider {
|
class FakeMutableFeatureFlagProvider(override val priority: Int) : MutableFeatureFlagProvider {
|
||||||
|
|
||||||
private val enabledFeatures = HashMap<String, Boolean>()
|
private val enabledFeatures = HashMap<String, Boolean>()
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue