diff --git a/src/LaunchDarkly.OpenFeature.ServerProvider/LaunchDarkly.OpenFeature.ServerProvider.csproj b/src/LaunchDarkly.OpenFeature.ServerProvider/LaunchDarkly.OpenFeature.ServerProvider.csproj
index dacef0e..254e5e2 100644
--- a/src/LaunchDarkly.OpenFeature.ServerProvider/LaunchDarkly.OpenFeature.ServerProvider.csproj
+++ b/src/LaunchDarkly.OpenFeature.ServerProvider/LaunchDarkly.OpenFeature.ServerProvider.csproj
@@ -41,7 +41,7 @@
-
+
diff --git a/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs b/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs
index 1435ee4..c04b0c2 100644
--- a/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs
+++ b/src/LaunchDarkly.OpenFeature.ServerProvider/Provider.cs
@@ -183,7 +183,7 @@ await EventChannel.Writer.WriteAsync(new ProviderEventPayload
{
ProviderName = _metadata.Name,
Type = ProviderEventTypes.ProviderConfigurationChanged,
- FlagsChanged = new List {changeEvent.Key},
+ FlagsChanged = new List { changeEvent.Key },
}).ConfigureAwait(false);
}
catch (Exception e)
@@ -217,5 +217,24 @@ private void StatusChangeHandler(object sender, DataSourceStatus status)
break;
}
}
+
+ ///
+ public override void Track(string trackingEventName, EvaluationContext evaluationContext = null, TrackingEventDetails trackingEventDetails = default)
+ {
+ var (value, details) = trackingEventDetails.ToLdValue();
+
+ if (value.HasValue)
+ {
+ _client.Track(trackingEventName, _contextConverter.ToLdContext(evaluationContext), details, value.Value);
+ }
+ else if (details.Type != LdValueType.Null)
+ {
+ _client.Track(trackingEventName, _contextConverter.ToLdContext(evaluationContext), details);
+ }
+ else
+ {
+ _client.Track(trackingEventName, _contextConverter.ToLdContext(evaluationContext));
+ }
+ }
}
}
diff --git a/src/LaunchDarkly.OpenFeature.ServerProvider/TrackingEventDetailsExtensions.cs b/src/LaunchDarkly.OpenFeature.ServerProvider/TrackingEventDetailsExtensions.cs
new file mode 100644
index 0000000..e5bc065
--- /dev/null
+++ b/src/LaunchDarkly.OpenFeature.ServerProvider/TrackingEventDetailsExtensions.cs
@@ -0,0 +1,39 @@
+using LaunchDarkly.Sdk;
+using OpenFeature.Model;
+
+namespace LaunchDarkly.OpenFeature.ServerProvider
+{
+ internal static class TrackingEventDetailsExtensions
+ {
+ ///
+ /// Extract an OpenFeature into an .
+ ///
+ /// The value to extract
+ public static (double?, LdValue) ToLdValue(this TrackingEventDetails trackingEventDetails)
+ {
+ if (trackingEventDetails == null)
+ {
+ return (null, LdValue.Null);
+ }
+
+ var value = trackingEventDetails.Value;
+
+ LdValue details;
+ if (trackingEventDetails.Count == 0)
+ {
+ details = LdValue.Null;
+ }
+ else
+ {
+ var builder = LdValue.BuildObject();
+ foreach (var keyvalue in trackingEventDetails)
+ {
+ builder.Add(keyvalue.Key, keyvalue.Value.ToLdValue());
+ }
+ details = builder.Build();
+ }
+
+ return (value, details);
+ }
+ }
+}
diff --git a/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/LaunchDarkly.OpenFeature.ServerProvider.Tests.csproj b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/LaunchDarkly.OpenFeature.ServerProvider.Tests.csproj
index 3209d74..e364126 100644
--- a/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/LaunchDarkly.OpenFeature.ServerProvider.Tests.csproj
+++ b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/LaunchDarkly.OpenFeature.ServerProvider.Tests.csproj
@@ -32,7 +32,7 @@
-
+
diff --git a/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ProviderTests.cs b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ProviderTests.cs
index f2a239c..ec642dd 100644
--- a/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ProviderTests.cs
+++ b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/ProviderTests.cs
@@ -272,5 +272,73 @@ public async Task ItEmitsConfigurationChangedEvents()
Assert.Single(eventPayloadB?.FlagsChanged ?? new List());
Assert.NotEqual(eventPayloadA?.FlagsChanged[0], eventPayloadB?.FlagsChanged[0]);
}
+
+ [Fact(Timeout = 5000)]
+ public void ItTracksCustomEvents()
+ {
+ var evaluationContext = EvaluationContext.Builder()
+ .Set("targetingKey", "the-key")
+ .Build();
+ var mock = new Mock();
+ mock.Setup(l => l.GetLogger())
+ .Returns(Components.NoLogging.Build(null).LogAdapter.Logger(null));
+ mock.Setup(l => l.Track("event-key-123abc", _converter.ToLdContext(evaluationContext))).Verifiable();
+ var provider = new Provider(mock.Object);
+
+ provider.Track("event-key-123abc", evaluationContext);
+
+ mock.Verify();
+ }
+
+ [Fact(Timeout = 5000)]
+ public void ItTracksCustomEventsWithValue()
+ {
+ var evaluationContext = EvaluationContext.Builder()
+ .Set("targetingKey", "the-key")
+ .Build();
+ var mock = new Mock();
+ mock.Setup(l => l.GetLogger())
+ .Returns(Components.NoLogging.Build(null).LogAdapter.Logger(null));
+ mock.Setup(l => l.Track("event-key-123abc", _converter.ToLdContext(evaluationContext), LdValue.Null, 99.77)).Verifiable();
+ var provider = new Provider(mock.Object);
+
+ provider.Track("event-key-123abc", evaluationContext, TrackingEventDetails.Builder().SetValue(99.77).Build());
+
+ mock.Verify();
+ }
+
+ [Fact(Timeout = 5000)]
+ public void ItTracksCustomEventsWithDetails()
+ {
+ var evaluationContext = EvaluationContext.Builder()
+ .Set("targetingKey", "the-key")
+ .Build();
+ var mock = new Mock();
+ mock.Setup(l => l.GetLogger())
+ .Returns(Components.NoLogging.Build(null).LogAdapter.Logger(null));
+ mock.Setup(l => l.Track("event-key-123abc", _converter.ToLdContext(evaluationContext), LdValue.BuildObject().Set("color", "red").Build())).Verifiable();
+ var provider = new Provider(mock.Object);
+
+ provider.Track("event-key-123abc", evaluationContext, TrackingEventDetails.Builder().Set("color", "red").Build());
+
+ mock.Verify();
+ }
+
+ [Fact(Timeout = 5000)]
+ public void ItTracksCustomEventsWithDetailsAndValue()
+ {
+ var evaluationContext = EvaluationContext.Builder()
+ .Set("targetingKey", "the-key")
+ .Build();
+ var mock = new Mock();
+ mock.Setup(l => l.GetLogger())
+ .Returns(Components.NoLogging.Build(null).LogAdapter.Logger(null));
+ mock.Setup(l => l.Track("event-key-123abc", _converter.ToLdContext(evaluationContext), LdValue.BuildObject().Set("currency", "USD").Build(), 99.77)).Verifiable();
+ var provider = new Provider(mock.Object);
+
+ provider.Track("event-key-123abc", evaluationContext, TrackingEventDetails.Builder().SetValue(99.77).Set("currency", "USD").Build());
+
+ mock.Verify();
+ }
}
}
diff --git a/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/TrackingEventDetailsExtensionsTest.cs b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/TrackingEventDetailsExtensionsTest.cs
new file mode 100644
index 0000000..748e774
--- /dev/null
+++ b/test/LaunchDarkly.OpenFeature.ServerProvider.Tests/TrackingEventDetailsExtensionsTest.cs
@@ -0,0 +1,56 @@
+using LaunchDarkly.Sdk;
+using OpenFeature.Model;
+using Xunit;
+
+namespace LaunchDarkly.OpenFeature.ServerProvider.Tests
+{
+ public class TrackingEventDetailsExtensionsTest
+ {
+ [Fact]
+ public void ItCanHandleValuesAndDetails()
+ {
+ var trackingEventDetails = TrackingEventDetails.Builder().SetValue(99.77).Set("currency", "USD").Build();
+ var (value, details) = trackingEventDetails.ToLdValue();
+ Assert.Equal(LdValueType.Object, details.Type);
+ Assert.Equal(LdValue.Of("USD"), details.Get("currency"));
+ Assert.Equal(99.77, value);
+ }
+
+ [Fact]
+ public void ItCanHandleDetailsOnly()
+ {
+ var trackingEventDetails = TrackingEventDetails.Builder().Set("color", "red").Build();
+ var (value, details) = trackingEventDetails.ToLdValue();
+ Assert.Equal(LdValueType.Object, details.Type);
+ Assert.Equal(LdValue.Of("red"), details.Get("color"));
+ Assert.Null(value);
+ }
+
+ [Fact]
+ public void ItCanHandleValuesOnly()
+ {
+ var trackingEventDetails = TrackingEventDetails.Builder().SetValue(99.77).Build();
+ var (value, details) = trackingEventDetails.ToLdValue();
+ Assert.Equal(LdValueType.Null, details.Type);
+ Assert.Equal(99.77, value);
+ }
+
+ [Fact]
+ public void ItCanHandleEmptyStructures()
+ {
+ var trackingEventDetails = TrackingEventDetails.Empty;
+ var (value, details) = trackingEventDetails.ToLdValue();
+ Assert.Equal(LdValueType.Null, details.Type);
+ Assert.Null(value);
+ }
+
+ [Fact]
+ public void ItCanHandleNull()
+ {
+ TrackingEventDetails trackingEventDetails = null;
+ var (value, details) = trackingEventDetails.ToLdValue();
+ Assert.Equal(LdValueType.Null, details.Type);
+ Assert.Null(value);
+ }
+ }
+}