diff --git a/build.gradle b/build.gradle
index b51359d..4730d00 100644
--- a/build.gradle
+++ b/build.gradle
@@ -107,12 +107,14 @@ nexusPublishing {
dependencies {
implementation group: 'com.launchdarkly', name: 'launchdarkly-java-server-sdk', version: '[7.1.0, 8.0.0)'
- implementation 'dev.openfeature:sdk:[1.7.0,2.0.0)'
+ implementation 'dev.openfeature:sdk:[1.14.0,2.0.0)'
// Use JUnit test framework
testImplementation(platform('org.junit:junit-bom:5.10.0'))
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation "org.mockito:mockito-core:3.+"
+
+ testImplementation 'org.awaitility:awaitility:4.2.0'
}
test {
diff --git a/src/main/java/com/launchdarkly/openfeature/serverprovider/Provider.java b/src/main/java/com/launchdarkly/openfeature/serverprovider/Provider.java
index ca58439..acb8e15 100644
--- a/src/main/java/com/launchdarkly/openfeature/serverprovider/Provider.java
+++ b/src/main/java/com/launchdarkly/openfeature/serverprovider/Provider.java
@@ -222,6 +222,38 @@ public void shutdown() {
}
}
+ @Override
+ public void track(String eventName, EvaluationContext ctx, TrackingEventDetails details) {
+ if (ctx == null) {
+ logger.info(
+ "The 'track' method was called with a null EvaluationContext. " +
+ "No 'track' event will be sent to LaunchDarkly. " +
+ "The LaunchDarkly SDK requires a non-null EvaluationContext.");
+ return;
+ }
+
+ if (details != null) {
+ Double metricValue = null;
+ if (details.getValue().isPresent()) {
+ metricValue = (Double) details.getValue().get();
+ }
+ // Convert the Structure portion of the TrackingEventDetails into a key value
+ // map.
+ // This will not put the metricValue extracted above into the map.
+ LDValue data = valueConverter.toLdValue(new Value(details));
+
+ if (metricValue != null) {
+ client.trackMetric(eventName, evaluationContextConverter.toLdContext(ctx), data, metricValue);
+ } else if (!data.isNull() && data.size() > 0) {
+ client.trackData(eventName, evaluationContextConverter.toLdContext(ctx), data);
+ } else {
+ client.track(eventName, evaluationContextConverter.toLdContext(ctx));
+ }
+ } else {
+ client.track(eventName, evaluationContextConverter.toLdContext(ctx));
+ }
+ }
+
/**
* Get the LaunchDarkly client associated with this provider.
*
diff --git a/src/main/java/com/launchdarkly/openfeature/serverprovider/Version.java b/src/main/java/com/launchdarkly/openfeature/serverprovider/Version.java
index fc7ca1a..ffd52fc 100644
--- a/src/main/java/com/launchdarkly/openfeature/serverprovider/Version.java
+++ b/src/main/java/com/launchdarkly/openfeature/serverprovider/Version.java
@@ -4,5 +4,5 @@ abstract class Version {
private Version() {}
// This constant is updated automatically by our Gradle script during a release, if the project version has changed
- static final String SDK_VERSION = "0.2.0";
+ static final String SDK_VERSION = "1.0.1";
}
diff --git a/src/test/java/com/launchdarkly/openfeature/serverprovider/ProviderTest.java b/src/test/java/com/launchdarkly/openfeature/serverprovider/ProviderTest.java
index 757a4c5..765078a 100644
--- a/src/test/java/com/launchdarkly/openfeature/serverprovider/ProviderTest.java
+++ b/src/test/java/com/launchdarkly/openfeature/serverprovider/ProviderTest.java
@@ -9,6 +9,9 @@
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
+
+import org.awaitility.Awaitility;
+
import static org.junit.jupiter.api.Assertions.*;
public class ProviderTest {
@@ -31,19 +34,24 @@ public void itCanDoABooleanEvaluation() {
EvaluationContext evaluationContext = new ImmutableContext("user-key");
when(mockedLdClient.boolVariationDetail("the-key", LDContext.create("user-key"), false))
- .thenReturn(EvaluationDetail.fromValue(true, 12, EvaluationReason.fallthrough()));
+ .thenReturn(EvaluationDetail.fromValue(true, 12, EvaluationReason.fallthrough()));
OpenFeatureAPI.getInstance().setProvider(ldProvider);
+ Awaitility.await().forever().until(() -> OpenFeatureAPI
+ .getInstance()
+ .getClient()
+ .getBooleanValue("the-key", false, evaluationContext));
+
assertTrue(OpenFeatureAPI
- .getInstance()
- .getClient()
- .getBooleanValue("the-key", false, evaluationContext));
+ .getInstance()
+ .getClient()
+ .getBooleanValue("the-key", false, evaluationContext));
FlagEvaluationDetails detailed = OpenFeatureAPI
- .getInstance()
- .getClient()
- .getBooleanDetails("the-key", false, evaluationContext);
+ .getInstance()
+ .getClient()
+ .getBooleanDetails("the-key", false, evaluationContext);
assertEquals(true, detailed.getValue());
assertEquals("12", detailed.getVariant());
@@ -55,20 +63,25 @@ public void itCanDoAStringEvaluation() {
EvaluationContext evaluationContext = new ImmutableContext("user-key");
when(mockedLdClient.stringVariationDetail("the-key", LDContext.create("user-key"), "default"))
- .thenReturn(EvaluationDetail
- .fromValue("evaluated", 17, EvaluationReason.off()));
+ .thenReturn(EvaluationDetail
+ .fromValue("evaluated", 17, EvaluationReason.off()));
OpenFeatureAPI.getInstance().setProvider(ldProvider);
+ Awaitility.await().forever().until(() -> OpenFeatureAPI
+ .getInstance()
+ .getClient()
+ .getStringValue("the-key", "default", evaluationContext).equals("evaluated"));
+
assertEquals("evaluated", OpenFeatureAPI
- .getInstance()
- .getClient()
- .getStringValue("the-key", "default", evaluationContext));
+ .getInstance()
+ .getClient()
+ .getStringValue("the-key", "default", evaluationContext));
FlagEvaluationDetails detailed = OpenFeatureAPI
- .getInstance()
- .getClient()
- .getStringDetails("the-key", "default", evaluationContext);
+ .getInstance()
+ .getClient()
+ .getStringDetails("the-key", "default", evaluationContext);
assertEquals("evaluated", detailed.getValue());
assertEquals("17", detailed.getVariant());
@@ -80,18 +93,24 @@ public void itCanDoADoubleEvaluation() {
EvaluationContext evaluationContext = new ImmutableContext("user-key");
when(mockedLdClient.doubleVariationDetail("the-key", LDContext.create("user-key"), 0.0))
- .thenReturn(EvaluationDetail.fromValue(1.0, 42, EvaluationReason.targetMatch()));
+ .thenReturn(EvaluationDetail.fromValue(1.0, 42, EvaluationReason.targetMatch()));
OpenFeatureAPI.getInstance().setProvider(ldProvider);
+
+ Awaitility.await().forever().until(() -> OpenFeatureAPI
+ .getInstance()
+ .getClient()
+ .getDoubleValue("the-key", 0.0, evaluationContext) != 0.0);
+
assertEquals(1.0, OpenFeatureAPI
- .getInstance()
- .getClient()
- .getDoubleValue("the-key", 0.0, evaluationContext), 0.00001);
+ .getInstance()
+ .getClient()
+ .getDoubleValue("the-key", 0.0, evaluationContext), 0.00001);
FlagEvaluationDetails detailed = OpenFeatureAPI
- .getInstance()
- .getClient()
- .getDoubleDetails("the-key", 0.0, evaluationContext);
+ .getInstance()
+ .getClient()
+ .getDoubleDetails("the-key", 0.0, evaluationContext);
assertEquals(1.0, detailed.getValue(), 0.00001);
assertEquals("42", detailed.getVariant());
@@ -122,8 +141,82 @@ public void itCanDoAValueEvaluation() {
.getObjectDetails("the-key", new Value(), evaluationContext);
assertEquals("aValue", detailed.getValue().asStructure().getValue("aKey").asString());
-
assertEquals("84", detailed.getVariant());
assertEquals("TARGETING_MATCH", detailed.getReason());
}
+
+ @Test
+ public void itCanTrackNoTrackingEventDetails() {
+ EvaluationContext evaluationContext = new ImmutableContext("user-key");
+ EvaluationContextConverter evaluationContextConverter = new EvaluationContextConverter(null);
+
+ OpenFeatureAPI.getInstance().setProvider(ldProvider);
+
+ OpenFeatureAPI
+ .getInstance()
+ .getClient().track("metric-key", evaluationContext);
+
+ verify(mockedLdClient).track(
+ "metric-key",
+ evaluationContextConverter.toLdContext(evaluationContext));
+ }
+
+ @Test
+ public void itCanTrackEmptyTrackingEventDetails() {
+ EvaluationContext evaluationContext = new ImmutableContext("user-key");
+ EvaluationContextConverter evaluationContextConverter = new EvaluationContextConverter(null);
+
+ TrackingEventDetails trackingEventDetails = new MutableTrackingEventDetails(null);
+
+ OpenFeatureAPI.getInstance().setProvider(ldProvider);
+
+ OpenFeatureAPI
+ .getInstance()
+ .getClient().track("metric-key", evaluationContext, trackingEventDetails);
+
+ verify(mockedLdClient).track(
+ "metric-key",
+ evaluationContextConverter.toLdContext(evaluationContext));
+ }
+
+ @Test
+ public void itCanTrackTrackingEventDetailsWithNoValue() {
+ EvaluationContext evaluationContext = new ImmutableContext("user-key");
+ EvaluationContextConverter evaluationContextConverter = new EvaluationContextConverter(null);
+ ValueConverter valueConverter = new ValueConverter(null);
+
+ TrackingEventDetails trackingEventDetails = new MutableTrackingEventDetails(null).add("currency", "USD");
+
+ OpenFeatureAPI.getInstance().setProvider(ldProvider);
+
+ OpenFeatureAPI
+ .getInstance()
+ .getClient().track("metric-key", evaluationContext, trackingEventDetails);
+
+ verify(mockedLdClient).trackData(
+ "metric-key",
+ evaluationContextConverter.toLdContext(evaluationContext),
+ valueConverter.toLdValue(new Value(trackingEventDetails)));
+ }
+
+ @Test
+ public void itCanTrackFullTrackingEventDetails() {
+ EvaluationContext evaluationContext = new ImmutableContext("user-key");
+ EvaluationContextConverter evaluationContextConverter = new EvaluationContextConverter(null);
+ ValueConverter valueConverter = new ValueConverter(null);
+
+ TrackingEventDetails trackingEventDetails = new MutableTrackingEventDetails(99.77).add("currency", "USD");
+
+ OpenFeatureAPI.getInstance().setProvider(ldProvider);
+
+ OpenFeatureAPI
+ .getInstance()
+ .getClient().track("metric-key", evaluationContext, trackingEventDetails);
+
+ verify(mockedLdClient).trackMetric(
+ "metric-key",
+ evaluationContextConverter.toLdContext(evaluationContext),
+ valueConverter.toLdValue(new Value(trackingEventDetails)),
+ 99.77);
+ }
}