-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add feature flag support using launchdarkly #336
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package io.newm.utils | ||
|
||
import android.app.Application | ||
import com.launchdarkly.sdk.ContextKind | ||
import com.launchdarkly.sdk.LDContext | ||
import com.launchdarkly.sdk.android.LDClient | ||
import com.launchdarkly.sdk.android.LDConfig | ||
import com.launchdarkly.sdk.android.LDConfig.Builder.AutoEnvAttributes | ||
import io.newm.shared.config.NewmSharedBuildConfig | ||
import io.newm.shared.public.featureflags.FeatureFlag | ||
import io.newm.shared.public.featureflags.FeatureFlagManager | ||
import kotlinx.coroutines.CompletableDeferred | ||
import kotlinx.coroutines.Deferred | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.withContext | ||
import java.util.concurrent.Future | ||
|
||
class AndroidFeatureFlagManager( | ||
private val application: Application, | ||
private val sharedBuildConfig: NewmSharedBuildConfig, | ||
) : FeatureFlagManager { | ||
|
||
private val client: LDClient = buildClient() | ||
|
||
private fun buildClient(): LDClient { | ||
val context = LDContext.builder(ContextKind.DEFAULT, "anonymous") | ||
.anonymous(true) | ||
.build() | ||
|
||
val ldConfig: LDConfig = LDConfig.Builder(AutoEnvAttributes.Enabled) | ||
.mobileKey(sharedBuildConfig.launchDarklyKey) | ||
.build() | ||
|
||
return LDClient.init(application, ldConfig, context, 0) | ||
} | ||
|
||
override fun isEnabled(flag: FeatureFlag, default: Boolean): Boolean { | ||
return client.boolVariation(flag.key, default) | ||
} | ||
|
||
override suspend fun setUserId(id: String) { | ||
val ldContext = LDContext.builder(ContextKind.DEFAULT, id).build() | ||
|
||
client.identify(ldContext) | ||
.asDeferred() | ||
.await() | ||
} | ||
} | ||
|
||
private suspend fun <V> Future<V>.asDeferred(): Deferred<V> { | ||
val deferred = CompletableDeferred<V>() | ||
|
||
withContext(Dispatchers.IO) { | ||
deferred.complete(get()) | ||
} | ||
|
||
return deferred | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package io.newm.shared.public.featureflags | ||
|
||
interface FeatureFlagManager { | ||
fun isEnabled(flag: FeatureFlag, default: Boolean = false): Boolean | ||
suspend fun setUserId(id: String) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package io.newm.shared.public.featureflags | ||
|
||
interface FeatureFlag { | ||
val key: String | ||
} | ||
|
||
object FeatureFlags { | ||
object MarketPlace : FeatureFlag { | ||
override val key = "streams-marketplace" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we set a default for this FF? Curious how we will handle the situation where feature flags were not fetched? I assume we use default values, and if so should those be declared here along with the key? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default value is passed when evaluating the flag. Boolean flags (the only ones we currently support) default to false but this can be changed by the caller to |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, so we will always attempt to fetch the FF, but if it fails we will use default values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, if we're unable to target this specific user, they will be targeted by the other rules we've defined in launchdarkly