Skip to content

Commit caa791c

Browse files
2.0.1 (#86)
* GetEnabledFeatures now returns sorted features list. (#69) * GetEnabledFeatures now returns sorted features list ignoring case * Removes sorting from GetEnabledFeatures method. (#73) * OASIS-2504 [C#] Release 2.0.0-beta1 (#72) * OASIS-2504 [C#] Release 2.0.0-beta1 (#71) * Revert "Eventdispatcherissue (#35) (#54)" This reverts commit 5df7cfb. * Bump version to 2.0 beta. * Update OptimizelySDK.nuspec * AssemblyVersion = 2.0.0.0 * Improved 1.3.1 DemoApp * Updated OptimizelySDK.Package and OptimizelySDK.nuspec * keypair.snk for *.sln, *.csproj * CHANGELOG.md corrections * SDK_VERSION synch with NuGet package version. * Make 'UserAttributes userAttributes = null' optional in 5 API's * Update verifysn.ps1 and DemoApp.csproj to 2.0.0-beta1 * Correct 2.0.0-beta1 release date in CHANGELOG.md * 2.0.0-beta1 --> 2.0.0 (#74) (#75) * Input validation in Activate, Track and GetVariation methods. (#76) * Input validation in Activate, Track and GetVariation methods. * Replaced InputType enum with string constants. * Updates unit tests. * Fix impression sent from feature experiment variation toggled off. * Revert "Fix impression sent from feature experiment variation toggled off." This reverts commit c943747. * Fix impression sent from feature experiment variation toggled off. (#84) * Update OptimizelySDK.DemoApp.csproj OptimizelySDK Reference * Update AssemblyVersion, AssemblyFileVersion, AssemblyInformationalVersion * Update the OptimizelySDK.Package/OptimizelySDK.nuspec * Upgrade DemoApp to OptimizelySDK 2.0.1
1 parent 5976b22 commit caa791c

File tree

10 files changed

+160
-66
lines changed

10 files changed

+160
-66
lines changed

OptimizelySDK.DemoApp/OptimizelySDK.DemoApp.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
<HintPath>..\packages\NJsonSchema.8.30.6304.31883\lib\net45\NJsonSchema.dll</HintPath>
129129
</Reference>
130130
<Reference Include="OptimizelySDK">
131-
<HintPath>..\packages\Optimizely.SDK.2.0.0-beta1\lib\net45\OptimizelySDK.dll</HintPath>
131+
<HintPath>..\packages\Optimizely.SDK.2.0.1\lib\net45\OptimizelySDK.dll</HintPath>
132132
</Reference>
133133
</ItemGroup>
134134
<ItemGroup>

OptimizelySDK.DemoApp/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@
3737
//
3838
// You can specify all the values or you can default the Revision and Build Numbers
3939
// by using the '*' as shown below:
40-
[assembly: AssemblyVersion("2.0.0.0")]
41-
[assembly: AssemblyFileVersion("2.0.0.0")]
42-
[assembly: AssemblyInformationalVersion("2.0.0")] // Used by Nuget.
40+
[assembly: AssemblyVersion("2.0.1.0")]
41+
[assembly: AssemblyFileVersion("2.0.1.0")]
42+
[assembly: AssemblyInformationalVersion("2.0.1")] // Used by Nuget.

OptimizelySDK.DemoApp/packages.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<package id="murmurhash-signed" version="1.0.2" targetFramework="net45" />
1919
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
2020
<package id="NJsonSchema" version="8.30.6304.31883" targetFramework="net45" />
21-
<package id="Optimizely.SDK" version="2.0.0" targetFramework="net45" />
21+
<package id="Optimizely.SDK" version="2.0.1" targetFramework="net45" />
2222
<package id="popper.js" version="1.12.9" targetFramework="net45" />
2323
<package id="Respond" version="1.2.0" targetFramework="net45" />
2424
<package id="WebGrease" version="1.5.2" targetFramework="net45" />

OptimizelySDK.Net35/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
// You can specify all the values or you can default the Build and Revision Numbers
3939
// by using the '*' as shown below:
4040
// [assembly: AssemblyVersion("1.0.*")]
41-
[assembly: AssemblyVersion("2.0.0.0")]
42-
[assembly: AssemblyFileVersion("2.0.0.0")]
43-
[assembly: AssemblyInformationalVersion("2.0.0")] // Used by Nuget.
41+
[assembly: AssemblyVersion("2.0.1.0")]
42+
[assembly: AssemblyFileVersion("2.0.1.0")]
43+
[assembly: AssemblyInformationalVersion("2.0.1")] // Used by Nuget.

OptimizelySDK.Net40/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
// You can specify all the values or you can default the Build and Revision Numbers
3939
// by using the '*' as shown below:
4040
// [assembly: AssemblyVersion("1.0.*")]
41-
[assembly: AssemblyVersion("2.0.0.0")]
42-
[assembly: AssemblyFileVersion("2.0.0.0")]
43-
[assembly: AssemblyInformationalVersion("2.0.0")] // Used by Nuget.
41+
[assembly: AssemblyVersion("2.0.1.0")]
42+
[assembly: AssemblyFileVersion("2.0.1.0")]
43+
[assembly: AssemblyInformationalVersion("2.0.1")] // Used by Nuget.

OptimizelySDK.Package/OptimizelySDK.nuspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<package>
33
<metadata>
44
<id>Optimizely.SDK</id>
5-
<version>2.0.0</version>
5+
<version>2.0.1</version>
66
<title>Optimizely C# SDK</title>
77
<authors>Optimizely Development Team</authors>
88
<owners>fullstack.optimizely</owners>

OptimizelySDK.Tests/OptimizelyTest.cs

+75-30
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class OptimizelyTest
3838
private ProjectConfig Config;
3939
private Mock<EventBuilder> EventBuilderMock;
4040
private Mock<IErrorHandler> ErrorHandlerMock;
41+
private Mock<IEventDispatcher> EventDispatcherMock;
4142
private Optimizely Optimizely;
4243
private IEventDispatcher EventDispatcher;
4344
private const string TestUserId = "testUserId";
@@ -73,19 +74,19 @@ public void Initialize()
7374
logger: LoggerMock.Object,
7475
errorHandler: new NoOpErrorHandler());
7576

76-
EventDispatcher = new ValidEventDispatcher();
77-
Optimizely = new Optimizely(TestData.Datafile, EventDispatcher, LoggerMock.Object, ErrorHandlerMock.Object);
77+
EventDispatcherMock = new Mock<IEventDispatcher>();
78+
Optimizely = new Optimizely(TestData.Datafile, EventDispatcherMock.Object, LoggerMock.Object, ErrorHandlerMock.Object);
7879

7980
Helper = new OptimizelyHelper
8081
{
8182
Datafile = TestData.Datafile,
82-
EventDispatcher = EventDispatcher,
83+
EventDispatcher = EventDispatcherMock.Object,
8384
Logger = LoggerMock.Object,
8485
ErrorHandler = ErrorHandlerMock.Object,
8586
SkipJsonValidation = false,
8687
};
8788

88-
OptimizelyMock = new Mock<Optimizely>(TestData.Datafile, EventDispatcher, LoggerMock.Object, ErrorHandlerMock.Object, null, false)
89+
OptimizelyMock = new Mock<Optimizely>(TestData.Datafile, EventDispatcherMock.Object, LoggerMock.Object, ErrorHandlerMock.Object, null, false)
8990
{
9091
CallBase = true
9192
};
@@ -1539,6 +1540,34 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsEnabledAndUserIsBeingExperimen
15391540
$@"Feature flag ""{featureKey}"" is enabled for user ""{TestUserId}""."));
15401541
}
15411542

1543+
// Should return false and send an impression event when feature is enabled for the user
1544+
// and user is being experimented.
1545+
[Test]
1546+
public void TestIsFeatureEnabledGivenFeatureFlagIsNotEnabledAndUserIsBeingExperimented()
1547+
{
1548+
var featureKey = "double_single_variable_feature";
1549+
var experiment = Config.GetExperimentFromKey("test_experiment_double_feature");
1550+
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "variation");
1551+
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
1552+
var decision = new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
1553+
1554+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
1555+
1556+
var optly = Helper.CreatePrivateOptimizely();
1557+
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
1558+
1559+
bool result = (bool)optly.Invoke("IsFeatureEnabled", featureKey, TestUserId, null);
1560+
Assert.False(result);
1561+
1562+
// SendImpressionEvent() gets called.
1563+
LoggerMock.Verify(l => l.Log(LogLevel.INFO,
1564+
$@"The user ""{TestUserId}"" is not being experimented on feature ""{featureKey}""."), Times.Never);
1565+
1566+
LoggerMock.Verify(l => l.Log(LogLevel.INFO,
1567+
$@"Feature flag ""{featureKey}"" is not enabled for user ""{TestUserId}""."));
1568+
EventDispatcherMock.Verify(dispatcher => dispatcher.DispatchEvent(It.IsAny<LogEvent>()));
1569+
}
1570+
15421571
// Verify that IsFeatureEnabled returns true if a variation does not get found in the feature
15431572
// flag experiment but found in the rollout rule.
15441573
[Test]
@@ -1811,36 +1840,52 @@ public void TestGetEnabledFeaturesWithSomeFeaturesEnabledForUser()
18111840
Array.ForEach(notEnabledFeatures, nef => CollectionAssert.DoesNotContain(actualFeaturesList, nef));
18121841
}
18131842

1843+
#endregion // Test GetEnabledFeatures
1844+
1845+
#region Test ValidateStringInputs
1846+
18141847
[Test]
1815-
public void TestGetEnabledFeaturesReturnsSortedList()
1848+
public void TestActivateValidateInputValues()
18161849
{
1817-
string[] unsortedFeaturesList =
1818-
{
1819-
"double_single_variable_feature",
1820-
"boolean_feature",
1821-
"string_single_variable_feature",
1822-
"multi_variate_feature",
1823-
"empty_feature",
1824-
"boolean_single_variable_feature"
1825-
};
1826-
string[] sortedFeaturesList =
1827-
{
1828-
"boolean_feature",
1829-
"boolean_single_variable_feature",
1830-
"double_single_variable_feature",
1831-
"empty_feature",
1832-
"multi_variate_feature",
1833-
"string_single_variable_feature",
1834-
};
1835-
1836-
OptimizelyMock.Setup(om => om.IsFeatureEnabled(It.IsIn<string>(unsortedFeaturesList), TestUserId,
1837-
It.IsAny<UserAttributes>())).Returns(true);
1850+
// Verify that ValidateStringInputs does not log error for valid values.
1851+
var variation = Optimizely.Activate("test_experiment", "test_user");
1852+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Never);
1853+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Experiment Key is in invalid format."), Times.Never);
18381854

1839-
// Verify that returned list in sorterd in ascending order.
1840-
var actualFeaturesList = OptimizelyMock.Object.GetEnabledFeatures(TestUserId, null);
1841-
CollectionAssert.AreEqual(sortedFeaturesList, actualFeaturesList);
1855+
// Verify that ValidateStringInputs logs error for invalid values.
1856+
variation = Optimizely.Activate("", null);
1857+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Once);
1858+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Experiment Key is in invalid format."), Times.Once);
18421859
}
18431860

1844-
#endregion // Test GetEnabledFeatures
1861+
[Test]
1862+
public void TestGetVariationValidateInputValues()
1863+
{
1864+
// Verify that ValidateStringInputs does not log error for valid values.
1865+
var variation = Optimizely.GetVariation("test_experiment", "test_user");
1866+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Never);
1867+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Experiment Key is in invalid format."), Times.Never);
1868+
1869+
// Verify that ValidateStringInputs logs error for invalid values.
1870+
variation = Optimizely.GetVariation("", null);
1871+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Once);
1872+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Experiment Key is in invalid format."), Times.Once);
1873+
}
1874+
1875+
[Test]
1876+
public void TestTrackValidateInputValues()
1877+
{
1878+
// Verify that ValidateStringInputs does not log error for valid values.
1879+
Optimizely.Track("purchase", "test_user");
1880+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Never);
1881+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Event Key is in invalid format."), Times.Never);
1882+
1883+
// Verify that ValidateStringInputs logs error for invalid values.
1884+
Optimizely.Track("", null);
1885+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Once);
1886+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Event Key is in invalid format."), Times.Once);
1887+
}
1888+
1889+
#endregion // Test ValidateStringInputs
18451890
}
18461891
}

OptimizelySDK.Tests/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@
3131
//
3232
// You can specify all the values or you can default the Revision and Build Numbers
3333
// by using the '*' as shown below:
34-
[assembly: AssemblyVersion("2.0.0.0")]
35-
[assembly: AssemblyFileVersion("2.0.0.0")]
36-
[assembly: AssemblyInformationalVersion("2.0.0")] // Used by Nuget.
34+
[assembly: AssemblyVersion("2.0.1.0")]
35+
[assembly: AssemblyFileVersion("2.0.1.0")]
36+
[assembly: AssemblyInformationalVersion("2.0.1")] // Used by Nuget.

OptimizelySDK/Optimizely.cs

+66-17
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class Optimizely
5151

5252
public static String SDK_VERSION {
5353
get {
54-
// Example output: "2.0.0" . Should be kept in synch with NuGet package version.
54+
// Example output: "2.0.1" . Should be kept in synch with NuGet package version.
5555
#if NET35
5656
Assembly assembly = Assembly.GetExecutingAssembly();
5757
#else
@@ -71,6 +71,10 @@ public static String SDK_TYPE {
7171
}
7272
}
7373

74+
public const string USER_ID = "User Id";
75+
public const string EXPERIMENT_KEY = "Experiment Key";
76+
public const string EVENT_KEY = "Event Key";
77+
7478
/// <summary>
7579
/// Optimizely constructor for managing Full Stack .NET projects.
7680
/// </summary>
@@ -158,6 +162,15 @@ public Variation Activate(string experimentKey, string userId, UserAttributes us
158162
return null;
159163
}
160164

165+
var inputValues = new Dictionary<string, string>
166+
{
167+
{ USER_ID, userId },
168+
{ EXPERIMENT_KEY, experimentKey }
169+
};
170+
171+
if (!ValidateStringInputs(inputValues))
172+
return null;
173+
161174
var experiment = Config.GetExperimentFromKey(experimentKey);
162175

163176
if (experiment.Key == null)
@@ -210,6 +223,15 @@ public void Track(string eventKey, string userId, UserAttributes userAttributes
210223
return;
211224
}
212225

226+
var inputValues = new Dictionary<string, string>
227+
{
228+
{ USER_ID, userId },
229+
{ EVENT_KEY, eventKey }
230+
};
231+
232+
if (!ValidateStringInputs(inputValues))
233+
return;
234+
213235
var eevent = Config.GetEvent(eventKey);
214236

215237
if (eevent.Key == null)
@@ -290,6 +312,15 @@ public Variation GetVariation(string experimentKey, string userId, UserAttribute
290312
return null;
291313
}
292314

315+
var inputValues = new Dictionary<string, string>
316+
{
317+
{ USER_ID, userId },
318+
{ EXPERIMENT_KEY, experimentKey }
319+
};
320+
321+
if (!ValidateStringInputs(inputValues))
322+
return null;
323+
293324
Experiment experiment = Config.GetExperimentFromKey(experimentKey);
294325
if (experiment.Key == null)
295326
return null;
@@ -335,14 +366,12 @@ public Variation GetForcedVariation(string experimentKey, string userId)
335366
/// <returns>True if feature is enabled, false or null otherwise</returns>
336367
public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttributes userAttributes = null)
337368
{
338-
if (string.IsNullOrEmpty(userId))
339-
{
369+
if (string.IsNullOrEmpty(userId)) {
340370
Logger.Log(LogLevel.ERROR, "User ID must not be empty.");
341371
return false;
342372
}
343373

344-
if (string.IsNullOrEmpty(featureKey))
345-
{
374+
if (string.IsNullOrEmpty(featureKey)) {
346375
Logger.Log(LogLevel.ERROR, "Feature flag key must not be empty.");
347376
return false;
348377
}
@@ -355,19 +384,21 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
355384
return false;
356385

357386
var decision = DecisionService.GetVariationForFeature(featureFlag, userId, userAttributes);
358-
if (decision == null || !decision.Variation.IsFeatureEnabled)
359-
{
360-
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is not enabled for user ""{userId}"".");
361-
return false;
387+
if (decision != null) {
388+
if (decision.Source == FeatureDecision.DECISION_SOURCE_EXPERIMENT) {
389+
SendImpressionEvent(decision.Experiment, decision.Variation, userId, userAttributes);
390+
} else {
391+
Logger.Log(LogLevel.INFO, $@"The user ""{userId}"" is not being experimented on feature ""{featureKey}"".");
392+
}
393+
if (decision.Variation.IsFeatureEnabled) {
394+
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is enabled for user ""{userId}"".");
395+
return true;
396+
}
362397
}
363398

364-
if (decision.Source == FeatureDecision.DECISION_SOURCE_EXPERIMENT)
365-
SendImpressionEvent(decision.Experiment, decision.Variation, userId, userAttributes);
366-
else
367-
Logger.Log(LogLevel.INFO, $@"The user ""{userId}"" is not being experimented on feature ""{featureKey}"".");
368399

369-
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is enabled for user ""{userId}"".");
370-
return true;
400+
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is not enabled for user ""{userId}"".");
401+
return false;
371402
}
372403

373404
/// <summary>
@@ -591,11 +622,29 @@ public List<string> GetEnabledFeatures(string userId, UserAttributes userAttribu
591622
enabledFeaturesList.Add(featureKey);
592623
}
593624

594-
enabledFeaturesList.Sort();
595-
596625
return enabledFeaturesList;
597626
}
598627

599628
#endregion // FeatureFlag APIs
629+
630+
/// <summary>
631+
/// Validate all string inputs are not null or empty.
632+
/// </summary>
633+
/// <param name="inputs">Array Hash input types and values</param>
634+
/// <returns>True if all values are valid, false otherwise</returns>
635+
private bool ValidateStringInputs(Dictionary<string, string> inputs)
636+
{
637+
bool isValid = true;
638+
foreach(var input in inputs)
639+
{
640+
if (string.IsNullOrEmpty(input.Value))
641+
{
642+
Logger.Log(LogLevel.ERROR, $"Provided {input.Key} is in invalid format.");
643+
isValid = false;
644+
}
645+
}
646+
647+
return isValid;
648+
}
600649
}
601650
}

OptimizelySDK/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
// You can specify all the values or you can default the Build and Revision Numbers
3939
// by using the '*' as shown below:
4040
// [assembly: AssemblyVersion("1.0.*")]
41-
[assembly: AssemblyVersion("2.0.0.0")]
42-
[assembly: AssemblyFileVersion("2.0.0.0")]
43-
[assembly: AssemblyInformationalVersion("2.0.0")] // Used by Nuget.
41+
[assembly: AssemblyVersion("2.0.1.0")]
42+
[assembly: AssemblyFileVersion("2.0.1.0")]
43+
[assembly: AssemblyInformationalVersion("2.0.1")] // Used by Nuget.

0 commit comments

Comments
 (0)