Skip to content

Commit

Permalink
Add ability to fetch variation detail for json and json list values (#5)
Browse files Browse the repository at this point in the history
* Add json variation detail to the getJsonValue function.

* Add support for fetching json value and list variation detail

* Fix detekt issues.

* Changes to pass klint check
  • Loading branch information
jacao authored Jul 5, 2024
1 parent 15a9a1a commit 09a0272
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 60 deletions.
2 changes: 1 addition & 1 deletion library/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ signing.keyId=""
signing.password=""

MODULE_PACKAGE_NAME=com.myunidays
MODULE_VERSION_NUMBER=0.1.3
MODULE_VERSION_NUMBER=0.1.4
MODULE_NAME=launchdarkly

OPEN_SOURCE_REPO=https://oss.sonatype.org/service/local/staging/deploy/maven2/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
package com.myunidays.launchdarkly

actual class EvaluationDetail<T> internal constructor(
val android: com.launchdarkly.sdk.EvaluationDetail<T>
val android: com.launchdarkly.sdk.EvaluationDetail<T>,
) : EvaluationDetailInterface<T> {

companion object {

fun <T> fromValues(
value: T?,
variationIndex: Int,
reason: com.launchdarkly.sdk.EvaluationReason?
): EvaluationDetail<T> {
val sdkDetail = createEvaluationDetail(value, variationIndex, reason)
return EvaluationDetail(sdkDetail)
}

private fun <T> createEvaluationDetail(
value: T?,
variationIndex: Int,
reason: com.launchdarkly.sdk.EvaluationReason?
): com.launchdarkly.sdk.EvaluationDetail<T> {
// Use reflection to access the private constructor
val constructor = com.launchdarkly.sdk.EvaluationDetail::class.constructors.first()
return constructor.call(value, variationIndex, reason) as com.launchdarkly.sdk.EvaluationDetail<T>
}
}

override val value: T?
get() = android.value
override val variationIndex: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,32 +83,82 @@ actual class LDClient actual constructor(
defaultValue: LDValue
): LDValue = LDValue(android.jsonValueVariation(key, defaultValue.android))

actual fun jsonValueVariationDetail(
key: String,
defaultValue: LDValue
): EvaluationDetailInterface<LDValue> = android!!.jsonValueVariationDetail(
key,
defaultValue.android
).let { evaluationDetail ->
EvaluationDetail.fromValues(
value = LDValue(evaluationDetail.value),
variationIndex = evaluationDetail.variationIndex,
reason = evaluationDetail.reason
)
}

actual fun <T> jsonValueVariation(
key: String,
deserializer: KSerializer<T>
): T? =
android.jsonValueVariation(key, com.launchdarkly.sdk.LDValue.ofNull())
.takeUnless { it.isNull }
?.let {
json.decodeFromString(
deserializer,
it.toJsonString()
)
}
this.jsonValueVariationDetail(key, deserializer).value

actual fun <T> jsonValueVariationDetail(
key: String,
deserializer: KSerializer<T>
): EvaluationDetailInterface<T?> =
android.jsonValueVariationDetail(key, com.launchdarkly.sdk.LDValue.ofNull()).let { evaluationDetail ->
evaluationDetail.value.takeUnless { it.isNull }
?.let {
json.decodeFromString(
deserializer,
it.toJsonString()
)
}.let { value ->
EvaluationDetail.fromValues(
value = value,
variationIndex = evaluationDetail.variationIndex,
reason = evaluationDetail.reason
)
}
}

actual fun <T> jsonListValueVariation(
key: String,
deserializer: KSerializer<T>
): List<T> =
android.jsonValueVariation(key, com.launchdarkly.sdk.LDValue.ofNull())
.takeUnless { it.isNull }
?.let {
json.decodeFromString(
ListSerializer(deserializer),
it.toJsonString()
)
): List<T> = this.jsonListValueVariationDetail(key, deserializer).value ?: emptyList()

// Define a generic function that returns an EvaluationDetailInterface for a list of type T
// The function takes a key and a deserializer for type T as parameters
actual fun <T> jsonListValueVariationDetail(
key: String,
deserializer: KSerializer<T>
): EvaluationDetailInterface<List<T>> =

// Call the android-specific jsonValueVariationDetail method with the provided key
// and a default null value, then process the result using 'let'
android.jsonValueVariationDetail(key, com.launchdarkly.sdk.LDValue.ofNull())
.let { jsonEvaluationDetail ->

// Extract the value from the jsonEvaluationDetail if it's not null
jsonEvaluationDetail.value.takeUnless { it.isNull }
// If the value is not null, decode it into a list of type T using the provided deserializer
?.let {
json.decodeFromString(
ListSerializer(deserializer),
it.toJsonString()
)
}.let { value ->

// Construct an EvaluationDetail object from the decoded value or an empty list if null,
// along with the variation index and reason from jsonEvaluationDetail
EvaluationDetail.fromValues(
value = value ?: emptyList(),
variationIndex = jsonEvaluationDetail.variationIndex,
reason = jsonEvaluationDetail.reason
)
}
}
?: emptyList()

actual fun identify(context: LDContext) {
android.identify(context.android)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ expect class LDClient(
fun stringVariation(key: String, defaultValue: String): String
fun stringVariationDetail(key: String, defaultValue: String): EvaluationDetailInterface<String>
fun jsonValueVariation(key: String, defaultValue: LDValue): LDValue
fun jsonValueVariationDetail(key: String, defaultValue: LDValue): EvaluationDetailInterface<LDValue>
fun <T> jsonValueVariation(key: String, deserializer: KSerializer<T>): T?
fun <T> jsonValueVariationDetail(key: String, deserializer: KSerializer<T>): EvaluationDetailInterface<T?>
fun <T> jsonListValueVariation(key: String, deserializer: KSerializer<T>): List<T>
fun <T> jsonListValueVariationDetail(key: String, deserializer: KSerializer<T>): EvaluationDetailInterface<List<T>>
fun close()
fun identify(context: LDContext)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.myunidays.launchdarkly

class JsonValueEvaluationDetail<T> internal constructor(
private val ios: cocoapods.LaunchDarkly.LDJSONEvaluationDetail,
override val value: T
) :
EvaluationDetailInterface<T> {
override val variationIndex: Int
get() = ios.variationIndex().toInt()
override val reason: EvaluationReason?
get() = ios.reason()?.let { EvaluationReason(it) }
}
127 changes: 86 additions & 41 deletions library/src/iosMain/kotlin/com/myunidays/launchdarkly/LDClient.ios.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ actual class LDClient actual constructor(
?.map { it.key as String to LDValue(it.value as cocoapods.LaunchDarkly.LDValue) }
?.toMap()
?: emptyMap()

init {
cocoapods.LaunchDarkly.LDClient.startWithConfiguration(config.ios, context.ios) {
ios = cocoapods.LaunchDarkly.LDClient.get()
Expand Down Expand Up @@ -90,57 +91,101 @@ actual class LDClient actual constructor(
defaultValue: LDValue
): LDValue = LDValue(ios!!.jsonVariationForKey(key, defaultValue.ios))

actual fun jsonValueVariationDetail(
key: String,
defaultValue: LDValue
): EvaluationDetailInterface<LDValue> = ios!!.jsonVariationDetailForKey(key, defaultValue.ios).let {
JsonValueEvaluationDetail(it, LDValue(it.value()))
}

actual fun <T> jsonValueVariation(
key: String,
deserializer: KSerializer<T>
): T? =
ios?.jsonVariationForKey(key, cocoapods.LaunchDarkly.LDValue.ofNull())
.takeUnless { it?.getType() == cocoapods.LaunchDarkly.LDValueTypeNull }
?.let { remoteValue ->
json.decodeFromString(
deserializer,
JsonObject(
remoteValue.dictValue()
.mapNotNull {
runCatching {
it.key as String to it.value as cocoapods.LaunchDarkly.LDValue
}.getOrNull()
}.associate { it.first to JsonPrimitive(it.second.stringValue()) }
).toString()
)
this.jsonValueVariationDetail(key, deserializer)?.value

actual fun <T> jsonValueVariationDetail(
key: String,
deserializer: KSerializer<T>
): EvaluationDetailInterface<T?> =
ios!!.jsonVariationDetailForKey(key, cocoapods.LaunchDarkly.LDValue.ofNull())
.let { jsonEvaluationDetail ->
jsonEvaluationDetail.value().takeUnless { it?.getType() == cocoapods.LaunchDarkly.LDValueTypeNull }
?.let { remoteValue ->
json.decodeFromString(
deserializer,
JsonObject(
remoteValue.dictValue()
.mapNotNull {
runCatching {
it.key as String to it.value as cocoapods.LaunchDarkly.LDValue
}.getOrNull()
}.associate { it.first to JsonPrimitive(it.second.stringValue()) }
).toString()
)
}.let { value ->
JsonValueEvaluationDetail(jsonEvaluationDetail, value)
}
}

actual fun <T> jsonListValueVariation(
key: String,
deserializer: KSerializer<T>
): List<T> =
ios?.jsonVariationForKey(key, cocoapods.LaunchDarkly.LDValue.ofNull())
.takeUnless { it?.getType() == cocoapods.LaunchDarkly.LDValueTypeNull }
?.let { remoteValue ->
json.decodeFromString(
ListSerializer(deserializer),
JsonArray(
remoteValue.arrayValue()
.filterIsInstance<cocoapods.LaunchDarkly.LDValue>()
.map { singleRemoteValue ->
JsonObject(
singleRemoteValue.dictValue()
.mapNotNull {
runCatching {
it.key as String to
it.value as cocoapods.LaunchDarkly.LDValue
}
.getOrNull()
}
.associate {
it.first to JsonPrimitive(it.second.stringValue())
}
)
}
).toString()
)
): List<T> = this.jsonListValueVariationDetail(key, deserializer).value ?: emptyList()

// Define a generic function that returns an EvaluationDetailInterface for a list of type T
// The function takes a key and a deserializer for type T as parameters
actual fun <T> jsonListValueVariationDetail(
key: String,
deserializer: KSerializer<T>
): EvaluationDetailInterface<List<T>> =

// Call the iOS-specific jsonVariationDetailForKey method with the provided key
// and a default null value, then process the result using 'let'
ios!!.jsonVariationDetailForKey(key, cocoapods.LaunchDarkly.LDValue.ofNull())
.let { jsonEvaluationDetail ->

// Extract the value from the jsonEvaluationDetail if its type is not Null
jsonEvaluationDetail.value().takeUnless { it.getType() == cocoapods.LaunchDarkly.LDValueTypeNull }

// If the value is not null, process the remote value
?.let { remoteValue ->

// Decode the remote value into a list of type T using the provided deserializer
json.decodeFromString(
ListSerializer(deserializer),
// Convert the remote value to a JSON string
JsonArray(
// Filter the remote value array to instances of LDValue
remoteValue.arrayValue()
.filterIsInstance<cocoapods.LaunchDarkly.LDValue>()
// Map each single remote value to a JsonObject
.map { singleRemoteValue ->

JsonObject(
// Convert the dictionary of the single remote value
// to a map of string keys and JsonPrimitive values
singleRemoteValue.dictValue()
.mapNotNull {
runCatching {
it.key as String to
it.value as cocoapods.LaunchDarkly.LDValue
}
.getOrNull()
}
.associate {
it.first to JsonPrimitive(it.second.stringValue())
}
)
}
).toString()
).let { value ->
// Construct a JsonValueEvaluationDetail object from the decoded value
JsonValueEvaluationDetail(jsonEvaluationDetail, value)
}
} // Return an empty list if the value is null
?: JsonValueEvaluationDetail(jsonEvaluationDetail, emptyList())
}
?: emptyList()

actual fun identify(context: LDContext) {
ios?.identifyWithContext(context.ios)
Expand Down

0 comments on commit 09a0272

Please sign in to comment.