Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.

Commit d9459ae

Browse files
authored
prepare 5.8.0 release (#110)
1 parent 1187294 commit d9459ae

File tree

13 files changed

+388
-92
lines changed

13 files changed

+388
-92
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to the LaunchDarkly .NET Server-Side SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
44

5+
## [5.8.0] - 2019-10-01
6+
### Added:
7+
- Added support for upcoming LaunchDarkly experimentation features. See `ILdClient.Track(string, User, LdValue, double)`.
8+
9+
### Fixed:
10+
- Fixed a bug due to incorrect use of a lock that could cause a read from `InMemoryFeatureStore` to fail if done at the same time as an update. (Thanks, [JeffAshton](https://github.com/launchdarkly/dotnet-server-sdk/pull/109)!)
11+
512
## [5.7.1] - 2019-09-13
613
_(The changes below were originally released in 5.7.0, but that release was broken; 5.7.1 is its replacement.)_
714

src/LaunchDarkly.ServerSdk/FeatureFlag.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ internal class FeatureFlag : IVersionedData, IFlagEventProperties
3333
internal List<JToken> Variations { get; private set; }
3434
[JsonProperty(PropertyName = "trackEvents")]
3535
public bool TrackEvents { get; private set; }
36+
[JsonProperty(PropertyName = "trackEventsFallthrough")]
37+
public bool TrackEventsFallthrough { get; private set; }
3638
[JsonProperty(PropertyName = "debugEventsUntilDate")]
3739
public long? DebugEventsUntilDate { get; private set; }
3840
[JsonProperty(PropertyName = "deleted")]
@@ -43,7 +45,7 @@ internal class FeatureFlag : IVersionedData, IFlagEventProperties
4345
[JsonConstructor]
4446
internal FeatureFlag(string key, int version, bool on, List<Prerequisite> prerequisites, string salt,
4547
List<Target> targets, List<Rule> rules, VariationOrRollout fallthrough, int? offVariation,
46-
List<JToken> variations, bool trackEvents, long? debugEventsUntilDate,
48+
List<JToken> variations, bool trackEvents, bool trackEventsFallthrough, long? debugEventsUntilDate,
4749
bool deleted, bool clientSide)
4850
{
4951
Key = key;
@@ -57,6 +59,7 @@ internal FeatureFlag(string key, int version, bool on, List<Prerequisite> prereq
5759
OffVariation = offVariation;
5860
Variations = variations;
5961
TrackEvents = trackEvents;
62+
TrackEventsFallthrough = trackEventsFallthrough;
6063
DebugEventsUntilDate = debugEventsUntilDate;
6164
Deleted = deleted;
6265
ClientSide = clientSide;
@@ -66,6 +69,13 @@ internal FeatureFlag()
6669
{
6770
}
6871

72+
internal FeatureFlag(string key, int version, bool deleted)
73+
{
74+
Key = key;
75+
Version = version;
76+
Deleted = deleted;
77+
}
78+
6979
internal struct EvalResult
7080
{
7181
internal EvaluationDetail<LdValue> Result;
@@ -77,13 +87,21 @@ internal EvalResult(EvaluationDetail<LdValue> result, IList<FeatureRequestEvent>
7787
PrerequisiteEvents = events;
7888
}
7989
}
80-
90+
8191
int IFlagEventProperties.EventVersion => Version;
8292

8393
// This method is called by EventFactory to determine if extra tracking should be
84-
// enabled for an event, based on the evaluation reason. It is not enabled yet.
94+
// enabled for an event, based on the evaluation reason.
8595
bool IFlagEventProperties.IsExperiment(EvaluationReason reason)
8696
{
97+
switch (reason)
98+
{
99+
case EvaluationReason.Fallthrough _:
100+
return TrackEventsFallthrough;
101+
case EvaluationReason.RuleMatch r:
102+
return r.RuleIndex >= 0 && Rules != null && r.RuleIndex < Rules.Count &&
103+
Rules[r.RuleIndex].TrackEvents;
104+
}
87105
return false;
88106
}
89107

src/LaunchDarkly.ServerSdk/ILdClient.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,22 @@ public interface ILdClient : ILdCommonClient
355355
// passing null for data will not be an ambiguous method call.
356356

357357
/// <summary>
358+
/// Tracks that a user performed an event, and provides an additional numeric value for custom metrics.
359+
/// </summary>
360+
/// <remarks>
361+
/// As of this version’s release date, the LaunchDarkly service does not support the <c>metricValue</c>
362+
/// parameter. As a result, calling this overload of <c>Track</c> will not yet produce any different
363+
/// behavior from calling <see cref="Track(string, JToken, User)"/> without a <c>metricValue</c>. Refer
364+
/// to the SDK reference guide for the latest status:
365+
/// https://docs.launchdarkly.com/docs/dotnet-sdk-reference#section-track
366+
/// </remarks>
367+
/// <param name="name">the name of the event</param>
368+
/// <param name="user">the user that performed the event</param>
369+
/// <param name="data">additional data associated with the event, or null</param>
370+
/// <param name="metricValue">A numeric value used by the LaunchDarkly experimentation feature in numeric custom
371+
/// metrics. This field will also be returned as part of the custom event for Data Export.</param>
372+
void Track(string name, User user, LdValue data, double metricValue);
373+
358374
/// Tracks that a user performed an event.
359375
/// </summary>
360376
/// <remarks>

src/LaunchDarkly.ServerSdk/LaunchDarkly.ServerSdk.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<Version>5.7.1</Version>
3+
<Version>5.8.0</Version>
44
<TargetFrameworks>netstandard1.4;netstandard1.6;netstandard2.0;net45</TargetFrameworks>
55
<PackageLicenseUrl>https://raw.githubusercontent.com/launchdarkly/dotnet-server-sdk/master/LICENSE</PackageLicenseUrl>
66
<DebugType>portable</DebugType>

src/LaunchDarkly.ServerSdk/LdClient.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,16 @@ public void Track(string name, User user, LdValue data)
432432
_eventProcessor.SendEvent(EventFactory.Default.NewCustomEvent(name, user, data));
433433
}
434434

435+
/// <inheritdoc/>
436+
public void Track(string name, User user, LdValue data, double metricValue)
437+
{
438+
if (user == null || user.Key == null)
439+
{
440+
Log.Warn("Track called with null user or null user key");
441+
}
442+
_eventProcessor.SendEvent(EventFactory.Default.NewCustomEvent(name, user, data, metricValue));
443+
}
444+
435445
/// <inheritdoc/>
436446
public void Identify(User user)
437447
{

src/LaunchDarkly.ServerSdk/Rule.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ internal class Rule : VariationOrRollout
1111
[JsonProperty(PropertyName = "clauses")]
1212
internal List<Clause> Clauses { get; private set; }
1313

14+
[JsonProperty(PropertyName = "trackEvents")]
15+
internal bool TrackEvents { get; private set; }
16+
1417
[JsonConstructor]
15-
internal Rule(string id, int? variation, Rollout rollout, List<Clause> clauses) : base(variation, rollout)
18+
internal Rule(string id, int? variation, Rollout rollout, List<Clause> clauses, bool trackEvents) : base(variation, rollout)
1619
{
1720
Id = id;
1821
Clauses = clauses;
22+
TrackEvents = trackEvents;
1923
}
2024

2125
internal bool MatchesUser(User user, IFeatureStore store)

src/LaunchDarkly.ServerSdk/VersionedDataKind.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ internal FeaturesVersionedDataKind() : base("features", typeof(FeatureFlag), "/f
115115

116116
public override FeatureFlag MakeDeletedItem(string key, int version)
117117
{
118-
return new FeatureFlag(key, version, false, null, "", null, null, null, null, null, false, null, true, false);
118+
return new FeatureFlag(key, version, true);
119119
}
120120

121121
public override IEnumerable<string> GetDependencyKeys(IVersionedData item)

test/LaunchDarkly.ServerSdk.Tests/FeatureFlagBuilder.cs

Lines changed: 138 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal class FeatureFlagBuilder
1919
private int? _offVariation;
2020
private List<JToken> _variations;
2121
private bool _trackEvents;
22+
private bool _trackEventsFallthrough;
2223
private long? _debugEventsUntilDate;
2324
private bool _deleted;
2425
private bool _clientSide;
@@ -41,6 +42,7 @@ internal FeatureFlagBuilder(FeatureFlag from)
4142
_offVariation = from.OffVariation;
4243
_variations = from.Variations;
4344
_trackEvents = from.TrackEvents;
45+
_trackEventsFallthrough = from.TrackEventsFallthrough;
4446
_debugEventsUntilDate = from.DebugEventsUntilDate;
4547
_deleted = from.Deleted;
4648
_clientSide = from.ClientSide;
@@ -50,7 +52,7 @@ internal FeatureFlag Build()
5052
{
5153
return new FeatureFlag(_key, _version, _on, _prerequisites, _salt,
5254
_targets, _rules, _fallthrough, _offVariation, _variations,
53-
_trackEvents, _debugEventsUntilDate, _deleted, _clientSide);
55+
_trackEvents, _trackEventsFallthrough, _debugEventsUntilDate, _deleted, _clientSide);
5456
}
5557

5658
internal FeatureFlagBuilder Version(int version)
@@ -71,6 +73,11 @@ internal FeatureFlagBuilder Prerequisites(List<Prerequisite> prerequisites)
7173
return this;
7274
}
7375

76+
internal FeatureFlagBuilder Prerequisites(params Prerequisite[] prerequisites)
77+
{
78+
return Prerequisites(new List<Prerequisite>(prerequisites));
79+
}
80+
7481
internal FeatureFlagBuilder Salt(string salt)
7582
{
7683
_salt = salt;
@@ -83,12 +90,22 @@ internal FeatureFlagBuilder Targets(List<Target> targets)
8390
return this;
8491
}
8592

93+
internal FeatureFlagBuilder Targets(params Target[] targets)
94+
{
95+
return Targets(new List<Target>(targets));
96+
}
97+
8698
internal FeatureFlagBuilder Rules(List<Rule> rules)
8799
{
88100
_rules = rules;
89101
return this;
90102
}
91103

104+
internal FeatureFlagBuilder Rules(params Rule[] rules)
105+
{
106+
return Rules(new List<Rule>(rules));
107+
}
108+
92109
internal FeatureFlagBuilder Fallthrough(VariationOrRollout fallthrough)
93110
{
94111
_fallthrough = fallthrough;
@@ -113,12 +130,23 @@ internal FeatureFlagBuilder Variations(List<JToken> variations)
113130
return this;
114131
}
115132

133+
internal FeatureFlagBuilder Variations(params JToken[] variations)
134+
{
135+
return Variations(new List<JToken>(variations));
136+
}
137+
116138
internal FeatureFlagBuilder TrackEvents(bool trackEvents)
117139
{
118140
_trackEvents = trackEvents;
119141
return this;
120142
}
121143

144+
internal FeatureFlagBuilder TrackEventsFallthrough(bool trackEventsFallthrough)
145+
{
146+
_trackEventsFallthrough = trackEventsFallthrough;
147+
return this;
148+
}
149+
122150
internal FeatureFlagBuilder DebugEventsUntilDate(long? debugEventsUntilDate)
123151
{
124152
_debugEventsUntilDate = debugEventsUntilDate;
@@ -139,19 +167,120 @@ internal FeatureFlagBuilder Deleted(bool deleted)
139167

140168
internal FeatureFlagBuilder OffWithValue(JToken value)
141169
{
142-
_on = false;
143-
_offVariation = 0;
144-
_variations = new List<JToken> { value };
145-
return this;
170+
return On(false).OffVariation(0).Variations(value);
146171
}
147172

148173
internal FeatureFlagBuilder BooleanWithClauses(params Clause[] clauses)
149174
{
150-
_on = true;
151-
_offVariation = 0;
152-
_variations = new List<JToken> { new JValue(false), new JValue(true) };
153-
_rules = new List<Rule> { new Rule("id", 1, null, new List<Clause>(clauses)) };
175+
return On(true).OffVariation(0)
176+
.Variations(new JValue(false), new JValue(true))
177+
.Rules(new RuleBuilder().Id("id").Variation(1).Clauses(clauses).Build());
178+
}
179+
}
180+
181+
internal class RuleBuilder
182+
{
183+
private string _id = "";
184+
private int? _variation = null;
185+
private Rollout _rollout = null;
186+
private List<Clause> _clauses = new List<Clause>();
187+
private bool _trackEvents = false;
188+
189+
internal Rule Build()
190+
{
191+
return new Rule(_id, _variation, _rollout, _clauses, _trackEvents);
192+
}
193+
194+
internal RuleBuilder Id(string id)
195+
{
196+
_id = id;
197+
return this;
198+
}
199+
200+
internal RuleBuilder Variation(int? variation)
201+
{
202+
_variation = variation;
203+
return this;
204+
}
205+
206+
internal RuleBuilder Rollout(Rollout rollout)
207+
{
208+
_rollout = rollout;
209+
return this;
210+
}
211+
212+
internal RuleBuilder Clauses(List<Clause> clauses)
213+
{
214+
_clauses = clauses;
215+
return this;
216+
}
217+
218+
internal RuleBuilder Clauses(params Clause[] clauses)
219+
{
220+
return Clauses(new List<Clause>(clauses));
221+
}
222+
223+
internal RuleBuilder TrackEvents(bool trackEvents)
224+
{
225+
_trackEvents = trackEvents;
226+
return this;
227+
}
228+
}
229+
230+
internal class ClauseBuilder
231+
{
232+
private string _attribute;
233+
private string _op;
234+
private List<JValue> _values = new List<JValue>();
235+
private bool _negate;
236+
237+
internal Clause Build()
238+
{
239+
return new Clause(_attribute, _op, _values, _negate);
240+
}
241+
242+
public ClauseBuilder Attribute(string attribute)
243+
{
244+
_attribute = attribute;
245+
return this;
246+
}
247+
248+
public ClauseBuilder Op(string op)
249+
{
250+
_op = op;
154251
return this;
155252
}
253+
254+
public ClauseBuilder Values(List<JValue> values)
255+
{
256+
_values = values;
257+
return this;
258+
}
259+
260+
public ClauseBuilder Values(params JValue[] values)
261+
{
262+
return Values(new List<JValue>(values));
263+
}
264+
265+
public ClauseBuilder Negate(bool negate)
266+
{
267+
_negate = negate;
268+
return this;
269+
}
270+
271+
public ClauseBuilder KeyIs(string key)
272+
{
273+
return Attribute("key").Op("in").Values(new JValue(key));
274+
}
275+
276+
public static Clause ShouldMatchUser(User user)
277+
{
278+
return new ClauseBuilder().KeyIs(user.Key).Build();
279+
}
280+
281+
public static Clause ShouldNotMatchUser(User user)
282+
{
283+
return new ClauseBuilder().KeyIs(user.Key).Negate(true).Build();
284+
}
156285
}
157286
}

0 commit comments

Comments
 (0)