Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ API can be used to wait until the provider is ready, or it has encountered a per
It the provider has been shutdown, because the OpenFeature API has been shutdown, or because the provider was no longer in use by the OpenFeature API, then the underlying LaunchDarkly SDK will be closed.
This is an important consideration if you are using the `getLdClient` method of the provider to access the underlying SDK instance.

### Flag Metadata

Evaluation details include flag metadata containing the parts of the LaunchDarkly evaluation reason that do not fit
into the OpenFeature reason and error code. Each entry is only present when it applies to the evaluation.

| Key | Type | Description |
|-----|------|-------------|
| `variationIndex` | integer | The index of the variation that was returned. Absent when the SDK returned the default value. |
| `inExperiment` | boolean | Only present, and always `true`, when the evaluation was part of an experiment. |
| `ruleIndex` | integer | The index of the targeting rule that matched. Only present for a `RULE_MATCH` reason. |
| `ruleId` | string | The identifier of the targeting rule that matched. Only present for a `RULE_MATCH` reason. |
| `prerequisiteKey` | string | The key of the prerequisite flag that failed. Only present for a `PREREQUISITE_FAILED` reason. |
| `bigSegmentsStatus` | string | The status of the Big Segments store, when the evaluation used Big Segments. |

### Examples

#### A single user context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.launchdarkly.sdk.EvaluationReason;
import com.launchdarkly.sdk.LDValue;
import dev.openfeature.sdk.ErrorCode;
import dev.openfeature.sdk.ImmutableMetadata;
import dev.openfeature.sdk.ProviderEvaluation;
import dev.openfeature.sdk.Reason;
import dev.openfeature.sdk.Value;
Expand All @@ -13,6 +14,13 @@
* Converts an EvaluationDetail into an OpenFeature ResolutionDetails.
*/
class EvaluationDetailConverter {
private static final String VARIATION_INDEX_KEY = "variationIndex";
private static final String IN_EXPERIMENT_KEY = "inExperiment";
private static final String RULE_INDEX_KEY = "ruleIndex";
private static final String RULE_ID_KEY = "ruleId";
private static final String PREREQUISITE_KEY_KEY = "prerequisiteKey";
private static final String BIG_SEGMENTS_STATUS_KEY = "bigSegmentsStatus";

LDLogger logger;
LDValueConverter ldValueConverter;

Expand Down Expand Up @@ -63,6 +71,31 @@ private static <T> ProviderEvaluation<T> getProviderEvaluation(T value, Evaluati
if (!isDefault) {
builder.variant(String.valueOf(variationIndex));
}
builder.flagMetadata(FlagMetadata(reason, isDefault, variationIndex));

return builder.build();
}

private static ImmutableMetadata FlagMetadata(EvaluationReason reason, boolean isDefault, int variationIndex) {
var builder = ImmutableMetadata.builder();
if (!isDefault) {
builder.addInteger(VARIATION_INDEX_KEY, variationIndex);
}
if (reason.isInExperiment()) {
builder.addBoolean(IN_EXPERIMENT_KEY, true);
}
if (reason.getKind() == EvaluationReason.Kind.RULE_MATCH) {
builder.addInteger(RULE_INDEX_KEY, reason.getRuleIndex());
if (reason.getRuleId() != null) {
builder.addString(RULE_ID_KEY, reason.getRuleId());
}
}
if (reason.getPrerequisiteKey() != null) {
builder.addString(PREREQUISITE_KEY_KEY, reason.getPrerequisiteKey());
}
if (reason.getBigSegmentsStatus() != null) {
builder.addString(BIG_SEGMENTS_STATUS_KEY, reason.getBigSegmentsStatus().name());
}

return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,79 @@ public void itCanHandleAnObjectResult() {

assertEquals(17.0, nestedList.get(0).asDouble(), EPSILON);
}

@Test
public void itIncludesTheVariationIndexInFlagMetadata() {
EvaluationDetail<Boolean> detail = EvaluationDetail.fromValue(
true, 3, EvaluationReason.fallthrough());

ImmutableMetadata metadata = evaluationDetailConverter.toEvaluationDetails(detail).getFlagMetadata();

assertEquals(3, metadata.getInteger("variationIndex"));
assertNull(metadata.getBoolean("inExperiment"));
}

@Test
public void itOmitsTheVariationIndexForDefaultValues() {
EvaluationDetail<Boolean> detail = EvaluationDetail.fromValue(
true, EvaluationDetail.NO_VARIATION,
EvaluationReason.error(EvaluationReason.ErrorKind.FLAG_NOT_FOUND));

ImmutableMetadata metadata = evaluationDetailConverter.toEvaluationDetails(detail).getFlagMetadata();

assertNull(metadata.getInteger("variationIndex"));
}

@Test
public void itIncludesExperimentationInformationInFlagMetadata() {
EvaluationDetail<Boolean> detail = EvaluationDetail.fromValue(
true, 1, EvaluationReason.fallthrough(true));

ImmutableMetadata metadata = evaluationDetailConverter.toEvaluationDetails(detail).getFlagMetadata();

assertEquals(true, metadata.getBoolean("inExperiment"));
}

@Test
public void itIncludesRuleInformationInFlagMetadata() {
EvaluationDetail<Boolean> detail = EvaluationDetail.fromValue(
true, 1, EvaluationReason.ruleMatch(4, "the-rule-id"));

ImmutableMetadata metadata = evaluationDetailConverter.toEvaluationDetails(detail).getFlagMetadata();

assertEquals(4, metadata.getInteger("ruleIndex"));
assertEquals("the-rule-id", metadata.getString("ruleId"));
}

@Test
public void itIncludesThePrerequisiteKeyInFlagMetadata() {
EvaluationDetail<Boolean> detail = EvaluationDetail.fromValue(
true, 0, EvaluationReason.prerequisiteFailed("the-prerequisite-key"));

ImmutableMetadata metadata = evaluationDetailConverter.toEvaluationDetails(detail).getFlagMetadata();

assertEquals("the-prerequisite-key", metadata.getString("prerequisiteKey"));
}

@Test
public void itIncludesTheBigSegmentsStatusInFlagMetadata() {
EvaluationDetail<Boolean> detail = EvaluationDetail.fromValue(
true, 0, EvaluationReason.fallthrough()
.withBigSegmentsStatus(EvaluationReason.BigSegmentsStatus.STALE));

ImmutableMetadata metadata = evaluationDetailConverter.toEvaluationDetails(detail).getFlagMetadata();

assertEquals("STALE", metadata.getString("bigSegmentsStatus"));
}

@Test
public void itIncludesFlagMetadataForObjectResults() {
EvaluationDetail<LDValue> detail = EvaluationDetail.fromValue(
new ObjectBuilder().put("aKey", "aValue").build(), 2, EvaluationReason.fallthrough(true));

ImmutableMetadata metadata = evaluationDetailConverter.toEvaluationDetailsLdValue(detail).getFlagMetadata();

assertEquals(2, metadata.getInteger("variationIndex"));
assertEquals(true, metadata.getBoolean("inExperiment"));
}
}
Loading