Skip to content

Commit

Permalink
Upgrade launchdarkly SDK from 5.10.7 to 6.3.0 (#3025)
Browse files Browse the repository at this point in the history
* Upgrade launchdarkly SDK from 5.10.7 to 6.3.0

* Fix broken test

* Fix broken test

---------

Co-authored-by: Andrew (Paradi) Alexander <adrw@squareup.com>
  • Loading branch information
pnsq and adrw authored Dec 14, 2023
1 parent 1616070 commit 0873023
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 50 deletions.
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ object Dependencies {
val kotlinxHtml = "org.jetbrains.kotlinx:kotlinx-html-jvm:0.8.1"
val kubernetesClient = "io.kubernetes:client-java:18.0.1"
val kubernetesClientApi = "io.kubernetes:client-java-api:18.0.1"
val launchDarkly = "com.launchdarkly:launchdarkly-java-server-sdk:5.10.7"
val launchDarkly = "com.launchdarkly:launchdarkly-java-server-sdk:6.3.0"
val logbackClassic = "ch.qos.logback:logback-classic:1.4.8"
val logbackCore = "ch.qos.logback:logback-core:1.4.8"
val mavenPublishGradlePlugin = "com.vanniktech:gradle-maven-publish-plugin:0.25.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package misk.feature.launchdarkly
import ch.qos.logback.classic.Level
import com.launchdarkly.sdk.EvaluationDetail
import com.launchdarkly.sdk.EvaluationReason
import com.launchdarkly.sdk.LDContext
import com.launchdarkly.sdk.LDUser
import com.launchdarkly.sdk.LDValue
import com.launchdarkly.sdk.UserAttribute
Expand Down Expand Up @@ -51,7 +52,7 @@ internal class LaunchDarklyFeatureFlagsTest {
@Test
fun getEnum() {
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"TYRANNOSAURUS", 1, EvaluationReason.targetMatch()
Expand All @@ -73,36 +74,36 @@ internal class LaunchDarklyFeatureFlagsTest {

assertThat(feature).isEqualTo(Dinosaur.TYRANNOSAURUS)

val userCaptor = ArgumentCaptor.forClass(LDUser::class.java)
val userCaptor = ArgumentCaptor.forClass(LDContext::class.java)
verify(client, times(1))
.stringVariationDetail(eq("which-dinosaur"), userCaptor.capture(), eq(""))

val user = userCaptor.value

// User fields are package-private, so we fetch it with reflection magicks!
val customField = LDUser::class.java.getDeclaredField("custom")
val customField = LDContext::class.java.getDeclaredField("attributes")
customField.isAccessible = true
@Suppress("unchecked_cast")
val customAttrs = customField.get(user) as Map<UserAttribute, LDValue>
val customAttrs = customField.get(user) as Map<String, LDValue>

val privateAttrsField = LDUser::class.java.getDeclaredField("privateAttributeNames")
val privateAttrsField = LDContext::class.java.getDeclaredField("privateAttributes")
privateAttrsField.isAccessible = true
@Suppress("unchecked_cast")
val privateAttrs = privateAttrsField.get(user) as Set<String>
val privateAttrs = privateAttrsField.get(user) as List<String>
val continent = UserAttribute.forName("continent")
val platform = UserAttribute.forName("platform")
val age = UserAttribute.forName("age")

assertThat(customAttrs.getValue(continent).stringValue()).isEqualTo("europa")
assertThat(customAttrs.getValue(platform).stringValue()).isEqualTo("lava")
assertThat(customAttrs.getValue(age).intValue()).isEqualTo(100000)
assertThat(privateAttrs).isEqualTo(setOf(continent, platform, age))
assertThat(customAttrs.getValue("continent").stringValue()).isEqualTo("europa")
assertThat(customAttrs.getValue("platform").stringValue()).isEqualTo("lava")
assertThat(customAttrs.getValue("age").intValue()).isEqualTo(100000)
assertThat(privateAttrs.toSet().equals(setOf(continent, platform, age)))
}

@Test
fun getEnumThrowsOnDefault() {
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"PTERODACTYL",
Expand All @@ -121,7 +122,7 @@ internal class LaunchDarklyFeatureFlagsTest {
@Test
fun getEnumThrowsOnEvalError() {
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"PTERODACTYL",
Expand All @@ -148,7 +149,7 @@ internal class LaunchDarklyFeatureFlagsTest {
Mockito
.`when`(
client.jsonValueVariationDetail(
anyString(), any(LDUser::class.java),
anyString(), any(LDContext::class.java),
any(LDValue::class.java)
)
)
Expand All @@ -173,7 +174,7 @@ internal class LaunchDarklyFeatureFlagsTest {
Mockito
.`when`(
client.jsonValueVariationDetail(
anyString(), any(LDUser::class.java),
anyString(), any(LDContext::class.java),
any(LDValue::class.java)
)
)
Expand Down Expand Up @@ -215,7 +216,7 @@ internal class LaunchDarklyFeatureFlagsTest {
@Test
fun attributes() {
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"value",
Expand All @@ -241,25 +242,25 @@ internal class LaunchDarklyFeatureFlagsTest {
val feature = featureFlags.getString(Feature("key"), "user", attributes)
assertThat(feature).isEqualTo("value")

val userCaptor = ArgumentCaptor.forClass(LDUser::class.java)
val userCaptor = ArgumentCaptor.forClass(LDContext::class.java)
verify(client, times(1))
.stringVariationDetail(eq("key"), userCaptor.capture(), eq(""))

val user = userCaptor.value
// NB: LDUser properties are package-local so we can't read them here.
// Create expected user and compare against actual.
val expected = LDUser.Builder("user")
.secondary("secondary value")
val expected = LDContext.fromUser(LDUser.Builder("user")
.ip("127.0.0.1")
.email("email@value.com")
.name("name value")
.avatar("avatar value")
.firstName("firstName value")
.lastName("lastName value")
.country("US")
.privateCustom("secondary", "secondary value")
.privateCustom("custom1", "custom1 value")
.privateCustom("custom2", "custom2 value")
.build()
.build())

// isEqualTo() would be more appropriate, since LDUser overrides equals(). However, failures would offer no
// meaningful output, given that LDUser does not override toString. Doing a field-by-field comparison is overkill
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ class LaunchDarklyModule @JvmOverloads constructor(
val ldConfig = LDConfig.Builder()
// Set wait to 0 to not block here. Block in service initialization instead.
.startWait(Duration.ofMillis(0))
.dataSource(Components.streamingDataSource().baseURI(baseUri))
.events(Components.sendEvents().baseURI(baseUri))
.dataSource(Components.streamingDataSource())
.events(Components.sendEvents())
.serviceEndpoints(Components.serviceEndpoints().relayProxy(baseUri))

config.ssl?.let {
val trustStore = sslLoader.loadTrustStore(config.ssl.trust_store)!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package wisp.launchdarkly

import com.launchdarkly.sdk.EvaluationDetail
import com.launchdarkly.sdk.EvaluationReason
import com.launchdarkly.sdk.LDContext
import com.launchdarkly.sdk.LDUser
import com.launchdarkly.sdk.LDValue
import com.launchdarkly.sdk.server.interfaces.LDClientInterface
Expand Down Expand Up @@ -112,22 +113,22 @@ class LaunchDarklyFeatureFlags @JvmOverloads constructor(

override fun getBoolean(feature: Feature, key: String, attributes: Attributes): Boolean =
get(feature, key, attributes) { name, user ->
ldClient.value.boolVariationDetail(name, user, false)
ldClient.value.boolVariationDetail(name, LDContext.fromUser(user), false)
}

override fun getDouble(feature: Feature, key: String, attributes: Attributes): Double =
get(feature, key, attributes) { name, user ->
ldClient.value.doubleVariationDetail(name, user, 0.0)
ldClient.value.doubleVariationDetail(name, LDContext.fromUser(user), 0.0)
}

override fun getInt(feature: Feature, key: String, attributes: Attributes): Int =
get(feature, key, attributes) { name, user ->
ldClient.value.intVariationDetail(name, user, 0)
ldClient.value.intVariationDetail(name, LDContext.fromUser(user), 0)
}

override fun getString(feature: Feature, key: String, attributes: Attributes): String =
get(feature, key, attributes) { name, user ->
ldClient.value.stringVariationDetail(name, user, "")
ldClient.value.stringVariationDetail(name, LDContext.fromUser(user), "")
}

override fun <T : Enum<T>> getEnum(
Expand All @@ -137,7 +138,7 @@ class LaunchDarklyFeatureFlags @JvmOverloads constructor(
attributes: Attributes,
): T {
val result = get(feature, key, attributes) { name, user ->
ldClient.value.stringVariationDetail(name, user, "")
ldClient.value.stringVariationDetail(name, LDContext.fromUser(user), "")
}
return java.lang.Enum.valueOf(clazz, result.uppercase(Locale.getDefault()))
}
Expand All @@ -149,7 +150,7 @@ class LaunchDarklyFeatureFlags @JvmOverloads constructor(
attributes: Attributes,
): T {
val result = get(feature, key, attributes) { name, user ->
ldClient.value.jsonValueVariationDetail(name, user, LDValue.ofNull())
ldClient.value.jsonValueVariationDetail(name, LDContext.fromUser(user), LDValue.ofNull())
}
return moshi.adapter(clazz)
.fromSafeJson(result.toJsonString()) { exception ->
Expand All @@ -160,7 +161,7 @@ class LaunchDarklyFeatureFlags @JvmOverloads constructor(

override fun getJsonString(feature: Feature, key: String, attributes: Attributes): String {
val result = get(feature, key, attributes) { name, user ->
ldClient.value.jsonValueVariationDetail(name, user, LDValue.ofNull())
ldClient.value.jsonValueVariationDetail(name, LDContext.fromUser(user), LDValue.ofNull())
}
return result.toJsonString()
}
Expand All @@ -176,7 +177,7 @@ class LaunchDarklyFeatureFlags @JvmOverloads constructor(
checkInitialized()
val listener = ldClient.value.flagTracker.addFlagValueChangeListener(
feature.name,
buildUser(feature, key, attributes)
LDContext.fromUser(buildUser(feature, key, attributes))
) { event ->
executor.execute {
tracker(mapper(event.newValue))
Expand Down Expand Up @@ -292,7 +293,6 @@ class LaunchDarklyFeatureFlags @JvmOverloads constructor(
when (k) {
// LaunchDarkly has some built-in keys that have to be initialized with their named
// methods.
"secondary" -> builder.secondary(v)
"ip" -> builder.ip(v)
"email" -> builder.email(v)
"name" -> builder.name(v)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal class LaunchDarklyFeatureFlagsTest {
@Test
fun getEnum() {
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"TYRANNOSAURUS", 1, EvaluationReason.targetMatch()
Expand All @@ -48,37 +48,37 @@ internal class LaunchDarklyFeatureFlagsTest {

assertThat(feature).isEqualTo(Dinosaur.TYRANNOSAURUS)

val userCaptor = ArgumentCaptor.forClass(LDUser::class.java)
val userCaptor = ArgumentCaptor.forClass(LDContext::class.java)
verify(client, times(1))
.stringVariationDetail(eq("which-dinosaur"), userCaptor.capture(), eq(""))

val user = userCaptor.value

// User fields are package-private, so we fetch it with reflection magicks!
val customField = LDUser::class.java.getDeclaredField("custom")
val customField = LDContext::class.java.getDeclaredField("attributes")
customField.isAccessible = true
@Suppress("unchecked_cast")
val customAttrs = customField.get(user) as Map<UserAttribute, LDValue>
val customAttrs = customField.get(user) as Map<String, LDValue>

val privateAttrsField = LDUser::class.java.getDeclaredField("privateAttributeNames")
val privateAttrsField = LDContext::class.java.getDeclaredField("privateAttributes")
privateAttrsField.isAccessible = true
@Suppress("unchecked_cast")
val privateAttrs = privateAttrsField.get(user) as Set<String>
val privateAttrs = privateAttrsField.get(user) as List<String>
val continent = UserAttribute.forName("continent")
val platform = UserAttribute.forName("platform")
val age = UserAttribute.forName("age")

assertThat(customAttrs.getValue(continent).stringValue()).isEqualTo("europa")
assertThat(customAttrs.getValue(platform).stringValue()).isEqualTo("lava")
assertThat(customAttrs.getValue(age).intValue()).isEqualTo(100000)
assertThat(privateAttrs).isEqualTo(setOf(continent, platform, age))
assertThat(customAttrs.getValue("continent").stringValue()).isEqualTo("europa")
assertThat(customAttrs.getValue("platform").stringValue()).isEqualTo("lava")
assertThat(customAttrs.getValue("age").intValue()).isEqualTo(100000)
assertThat(privateAttrs.toSet().equals(setOf(continent, platform, age)))
}

@Test
fun getEnumThrowsOnDefault() {
Mockito.`when`(client.isInitialized).thenReturn(false)
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"PTERODACTYL",
Expand All @@ -97,7 +97,7 @@ internal class LaunchDarklyFeatureFlagsTest {
@Test
fun getEnumThrowsOnEvalError() {
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"PTERODACTYL",
Expand All @@ -124,7 +124,7 @@ internal class LaunchDarklyFeatureFlagsTest {
Mockito
.`when`(
client.jsonValueVariationDetail(
anyString(), any(LDUser::class.java),
anyString(), any(LDContext::class.java),
any(LDValue::class.java)
)
)
Expand All @@ -149,7 +149,7 @@ internal class LaunchDarklyFeatureFlagsTest {
Mockito
.`when`(
client.jsonValueVariationDetail(
anyString(), any(LDUser::class.java),
anyString(), any(LDContext::class.java),
any(LDValue::class.java)
)
)
Expand Down Expand Up @@ -178,7 +178,7 @@ internal class LaunchDarklyFeatureFlagsTest {
@Test
fun attributes() {
Mockito
.`when`(client.stringVariationDetail(anyString(), any(LDUser::class.java), anyString()))
.`when`(client.stringVariationDetail(anyString(), any(LDContext::class.java), anyString()))
.thenReturn(
EvaluationDetail.fromValue(
"value",
Expand All @@ -204,25 +204,25 @@ internal class LaunchDarklyFeatureFlagsTest {
val feature = featureFlags.getString(Feature("key"), "user", attributes)
assertThat(feature).isEqualTo("value")

val userCaptor = ArgumentCaptor.forClass(LDUser::class.java)
val userCaptor = ArgumentCaptor.forClass(LDContext::class.java)
verify(client, times(1))
.stringVariationDetail(eq("key"), userCaptor.capture(), eq(""))

val user = userCaptor.value
// NB: LDUser properties are package-local so we can't read them here.
// Create expected user and compare against actual.
val expected = LDUser.Builder("user")
.secondary("secondary value")
val expected = LDContext.fromUser(LDUser.Builder("user")
.ip("127.0.0.1")
.email("email@value.com")
.name("name value")
.avatar("avatar value")
.firstName("firstName value")
.lastName("lastName value")
.country("US")
.privateCustom("secondary", "secondary value")
.privateCustom("custom1", "custom1 value")
.privateCustom("custom2", "custom2 value")
.build()
.build())

// isEqualTo() would be more appropriate, since LDUser overrides equals(). However, failures would offer no
// meaningful output, given that LDUser does not override toString. Doing a field-by-field comparison is overkill
Expand Down

0 comments on commit 0873023

Please sign in to comment.