Skip to content

Commit

Permalink
Remove support for primitive ByteArrayFeatureFlag
Browse files Browse the repository at this point in the history
Closes #49
  • Loading branch information
kevincianfarini committed Mar 6, 2024
1 parent 825c4d4 commit 16699d7
Show file tree
Hide file tree
Showing 16 changed files with 0 additions and 134 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ Monarch provides several feature flag types in the 'core' artifact.
* `LongFeatureFlag`
* `DoubleFeatureFlag`
* `StringFeatureFlag`
* `ByteArrayFeatureFlag`

### Obtaining values

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,3 @@ public abstract class LongFeatureFlag(
public override val key: String,
public override val default: Long,
) : FeatureFlag<Long>

/**
* A simple [Long] feature flag.
*/
public abstract class ByteArrayFeatureFlag(
public override val key: String,
public override val default: ByteArray,
) : FeatureFlag<ByteArray>
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,4 @@ public interface FeatureFlagDataStore {
* Get the [Long] value associated with [key] if present. Otherwise, return [default].
*/
public fun getLong(key: String, default: Long): Long

/**
* Get the [ByteArray] value associated with [key] if present. Otherwise, return [default].
*/
public fun getByteArray(key: String, default: ByteArray): ByteArray
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ public class InMemoryFeatureFlagDataStoreOverride(
return cache.getCachedValue<Long>(key) ?: delegate.getLong(key, default)
}

public override fun getByteArray(key: String, default: ByteArray): ByteArray {
return cache.getCachedValue<ByteArray>(key) ?: delegate.getByteArray(key, default)
}

public override fun observeBoolean(key: String, default: Boolean): Flow<Boolean> {
return cache.observeCachedValue<Boolean>(key).flatMapLatest { cachedValue ->
when (cachedValue) {
Expand Down Expand Up @@ -81,15 +77,6 @@ public class InMemoryFeatureFlagDataStoreOverride(
}
}

public override fun observeByteArray(key: String, default: ByteArray): Flow<ByteArray> {
return cache.observeCachedValue<ByteArray>(key).flatMapLatest { cachedValue ->
when (cachedValue) {
null -> delegate.observeByteArray(key, default)
else -> flowOf(cachedValue)
}
}
}

public fun setBoolean(key: String, value: Boolean) {
cache.update { map -> map + Pair(key, value) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public class MixinFeatureFlagManager(
is StringFeatureFlag -> store.getString(flag.key, flag.default) as T
is DoubleFeatureFlag -> store.getDouble(flag.key, flag.default) as T
is LongFeatureFlag -> store.getLong(flag.key, flag.default) as T
is ByteArrayFeatureFlag -> store.getByteArray(flag.key, flag.default) as T
else -> mixins.firstNotNullOfOrNull { delegate ->
delegate.currentValueOfOrNull(flag, store)
} ?: throw IllegalArgumentException("$flag is not a recognized feature flag.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,4 @@ public interface ObservableFeatureFlagDataStore : FeatureFlagDataStore {
* time of collection, and will emit again on each subsequent update for [key].
*/
public fun observeLong(key: String, default: Long): Flow<Long>

/**
* Return a [Flow] which emits updates to a [ByteArray] value associated with [key] if present. When no value is
* present for [key], the returned flow will emit [default]. The returned flow initially emits the value at the
* time of collection, and will emit again on each subsequent update for [key].
*/
public fun observeByteArray(key: String, default: ByteArray): Flow<ByteArray>
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.github.kevincianfarini.monarch

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/**
* A [ObservableFeatureFlagManager] implementation that allows extension via [mixins].
Expand All @@ -25,7 +24,6 @@ public class ObservableMixinFeatureFlagManager(
is StringFeatureFlag -> store.observeString(flag.key, flag.default) as Flow<T>
is DoubleFeatureFlag -> store.observeDouble(flag.key, flag.default) as Flow<T>
is LongFeatureFlag -> store.observeLong(flag.key, flag.default) as Flow<T>
is ByteArrayFeatureFlag -> store.observeByteArray(flag.key, flag.default) as Flow<T>
else -> mixins.firstNotNullOfOrNull { delegate ->
delegate.valuesOfOrNull(flag, store)
} ?: throw IllegalArgumentException("$flag is not a recognized feature flag.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class InMemoryFeatureFlagDataStoreOverrideTest {
Triple("correct", "incorrect") { getString(it, "incorrect") },
Triple(1.5, 3.0) { getDouble(it, 3.0) },
Triple(1L, 2L) { getLong(it, 2L) },
Triple(byteArrayOf(0b1), byteArrayOf(0b0)) { getByteArray(it, byteArrayOf(0b0)) }
).forEach { (overrideValue, delegateValue, produceFn) ->
testCacheOverridesDelegateSynchronousParameterized(overrideValue, delegateValue, produceFn)
}
Expand Down Expand Up @@ -47,7 +46,6 @@ class InMemoryFeatureFlagDataStoreOverrideTest {
Pair("correct") { getString(it, "incorrect") },
Pair(3.0) { getDouble(it, 1.5) },
Pair(2L) { getLong(it, 1L) },
Pair(byteArrayOf(0b0)) { getByteArray(it, byteArrayOf(0b1)) }
).forEach { (delegateValue, produceFn) ->
testCacheFallsBackToDelegateSynchronousParameterized(delegateValue, produceFn)
}
Expand All @@ -73,7 +71,6 @@ class InMemoryFeatureFlagDataStoreOverrideTest {
Triple("correct", "incorrect") { observeString(it, "incorrect") },
Triple(1.5, 3.0) { observeDouble(it, 3.0) },
Triple(1L, 2L) { observeLong(it, 2L) },
Triple(byteArrayOf(0b1), byteArrayOf(0b0)) { observeByteArray(it, byteArrayOf(0b0)) }
).forEach { (overrideValue, delegateValue, produceFn) ->
storeCacheOverridesDelegateFlowParameterized(overrideValue, delegateValue, produceFn)
}
Expand Down Expand Up @@ -105,7 +102,6 @@ class InMemoryFeatureFlagDataStoreOverrideTest {
Pair("correct") { observeString(it, "incorrect") },
Pair(3.0) { observeDouble(it, 1.5) },
Pair(2L) { observeLong(it, 1L) },
Pair(byteArrayOf(0b0)) { observeByteArray(it, byteArrayOf(0b1)) }
).forEach { (delegateValue, produceFn) ->
storeCacheFallsBackToDelegateFlowParameterized(delegateValue, produceFn)
}
Expand Down Expand Up @@ -133,7 +129,6 @@ class InMemoryFeatureFlagDataStoreOverrideTest {
Triple("correct", { setString(it, "also correct") }) { observeString(it, "incorrect") },
Triple(1.5, { setDouble(it, 3.0) }) { observeDouble(it, 4.5) },
Triple(1L, { setLong(it, 3L) }) { observeLong(it, 2L) },
Triple(byteArrayOf(0b1), { setByteArray(it, byteArrayOf(0b0)) }) { observeByteArray(it, byteArrayOf(0b101)) }
).forEach { (initialValue, newValue, produceFn) ->
writingToStoreCacheEmitsNewValueParameterized(initialValue, newValue, produceFn)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,6 @@ class MixinFeatureFlagManagerTest {
actual = manager().currentValueOf(LongFeature),
)

@Test fun `manager gets byte array value`() {
val store = InMemoryFeatureFlagDataStore().apply { setValue("byte", byteArrayOf(0b11)) }
assertContentEquals(
expected = byteArrayOf(0b11),
actual = manager(store).currentValueOf(ByteArrayFeature),
)
}

@Test fun `manager gets default byte array value`() = assertContentEquals(
expected = byteArrayOf(0b1),
actual = manager().currentValueOf(ByteArrayFeature),
)

@Test fun `manager gets mixin value`() {
val store = InMemoryFeatureFlagDataStore().apply { setValue("some_int", "1") }
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,6 @@ class ObservableMixinFeatureFlagManagerTest {
}
}

@Test fun `manager gets byte array value`() {
runBlocking {
val store = InMemoryFeatureFlagDataStore().apply { setValue("byte", byteArrayOf(0b11)) }
manager(store).valuesOf(ByteArrayFeature).test {
assertContentEquals(expected = byteArrayOf(0b11), actual = awaitItem())
cancelAndIgnoreRemainingEvents()
}
}
}

@Test fun `manager gets default byte array value`() {
runBlocking {
manager().valuesOf(ByteArrayFeature).test {
assertContentEquals(expected = byteArrayOf(0b1), actual = awaitItem())
cancelAndIgnoreRemainingEvents()
}
}
}

@Test fun `manager gets mixin value`() {
runBlocking {
val store = InMemoryFeatureFlagDataStore().apply { setValue("some_int", "1") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ object StringFeature : StringFeatureFlag(key = "foo", default = "blah")
object BooleanFeature : BooleanFeatureFlag(key = "bool", default = false)
object DoubleFeature : DoubleFeatureFlag(key = "double", default = 1.5)
object LongFeature : LongFeatureFlag(key = "long", default = 1027L)
object ByteArrayFeature : ByteArrayFeatureFlag(key = "byte", default = byteArrayOf(0b1))
object IntFeatureFlag : FeatureFlag<Int> {
override val key: String get() = "some_int"
override val default = -1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,4 @@ public class EnvironmentVariableFeatureFlagDataStore internal constructor(
val long = if (strictlyTyped) env?.toLong() else env?.toLongOrNull()
return long ?: default
}

override fun getByteArray(key: String, default: ByteArray): ByteArray {
val env = getEnvironmentVariable(key)
val bytes = if (strictlyTyped) env?.decodeHexToByteArray() else env?.decodeHexToByteArrayOrNull()
return bytes ?: default
}
}

private fun String.decodeHexToByteArrayOrNull(): ByteArray? = takeIf { it.length % 2 == 0 }?.let { string ->
ByteArray(string.length / 2) { index ->
val startIndex = index * 2
val endIndex = startIndex + 1
val byteString = string.substring(startIndex, endIndex + 1)
byteString.toByte(16)
}
}

private fun String.decodeHexToByteArray(): ByteArray {
return requireNotNull(decodeHexToByteArrayOrNull()) {
"The input string $this is not a valid hex string."
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,13 @@ private class LaunchDarklyFeatureFlagDataStore(
return client.getValue(key, default)
}

override fun getByteArray(key: String, default: ByteArray): ByteArray {
throw NotImplementedError("LaunchDarkly does not support ByteArray flags.")
}

override fun observeString(key: String, default: String): Flow<String> = client.observeValue(key, default)

override fun observeBoolean(key: String, default: Boolean): Flow<Boolean> = client.observeValue(key, default)

override fun observeDouble(key: String, default: Double): Flow<Double> = client.observeValue(key, default)

override fun observeLong(key: String, default: Long): Flow<Long> = client.observeValue(key, default)

override fun observeByteArray(key: String, default: ByteArray): Flow<ByteArray> {
throw NotImplementedError("LaunchDarkly does not support ByteArray flags.")
}
}

private inline fun <reified T : Any> LDClientInterface.observeValue(key: String, default: T): Flow<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,6 @@ class LaunchDarklyFeatureFlagDataStoreTest {
assertEquals("non_default", dataStore.getString("key", "default"))
}

@Test fun getting_byte_array_errors() = runTest {
val (dataStore, _) = sut()
assertFailsWith<NotImplementedError> {
dataStore.getByteArray("key", byteArrayOf())
}
}

@Test fun getting_string_from_json_flag_returns_value() {
val (dataStore, mutate) = sut()
val expected = Thing(1, 2)
Expand Down Expand Up @@ -164,13 +157,6 @@ class LaunchDarklyFeatureFlagDataStoreTest {
}
}

@Test fun observing_byte_array_errors() = runTest {
val (dataStore, _) = sut()
assertFailsWith<NotImplementedError> {
dataStore.observeByteArray("key", byteArrayOf())
}
}

@Test fun observing_json_string_emits_value_updates() = runTest {
val (dataStore, mutate) = sut()
val first = Thing(1, 2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ private class LaunchDarklyFeatureFlagDataStore(
return shim.getValue(key, default)
}

override fun getByteArray(key: String, default: ByteArray): ByteArray {
throw NotImplementedError("LaunchDarkly does not support ByteArray flags.")
}

override fun observeString(key: String, default: String): Flow<String> {
return shim.observeValue(key, default)
}
Expand All @@ -54,10 +50,6 @@ private class LaunchDarklyFeatureFlagDataStore(
override fun observeLong(key: String, default: Long): Flow<Long> {
return shim.observeValue(key, default)
}

override fun observeByteArray(key: String, default: ByteArray): Flow<ByteArray> {
throw NotImplementedError("LaunchDarkly does not support ByteArray flags.")
}
}

@OptIn(ExperimentalForeignApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ public class InMemoryFeatureFlagDataStore : ObservableFeatureFlagDataStore {
return store.observeValue(key, default)
}

public override fun observeByteArray(key: String, default: ByteArray): Flow<ByteArray> {
return store.observeValue(key, default)
}

public override fun getBoolean(key: String, default: Boolean): Boolean {
return store.getValue(key, default)
}
Expand All @@ -42,10 +38,6 @@ public class InMemoryFeatureFlagDataStore : ObservableFeatureFlagDataStore {
return store.getValue(key, default)
}

public override fun getByteArray(key: String, default: ByteArray): ByteArray {
return store.getValue(key, default)
}

public fun setValue(key: String, value: Any?) {
store.update { map ->
map.plus(key to value)
Expand Down

0 comments on commit 16699d7

Please sign in to comment.