Skip to content

Commit 6dedc61

Browse files
mfahadahmedmikeproeng37
authored andcommitted
feature(API): Adds input validation in all API methods and validates empty user Id. (#104)
1 parent 4fd7abf commit 6dedc61

File tree

4 files changed

+145
-108
lines changed

4 files changed

+145
-108
lines changed

OptimizelySDK.Tests/OptimizelyTest.cs

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,34 @@ public void TestSetForcedVariation()
943943
Assert.IsTrue(TestData.CompareObjects(VariationWithKeyVariation, actualForcedVariation));
944944
}
945945

946+
[Test]
947+
public void TestSetForcedVariationWithNullAndEmptyUserId()
948+
{
949+
Assert.False(Optimizely.SetForcedVariation("test_experiment", null, "variation"));
950+
Assert.True(Optimizely.SetForcedVariation("test_experiment", "", "variation"));
951+
}
952+
953+
[Test]
954+
public void TestSetForcedVariationWithInvalidExperimentKey()
955+
{
956+
var userId = "test_user";
957+
var variation = "variation";
958+
959+
Assert.False(Optimizely.SetForcedVariation("test_experiment_not_in_datafile", userId, variation));
960+
Assert.False(Optimizely.SetForcedVariation("", userId, variation));
961+
Assert.False(Optimizely.SetForcedVariation(null, userId, variation));
962+
}
963+
964+
[Test]
965+
public void TestSetForcedVariationWithInvalidVariationKey()
966+
{
967+
var userId = "test_user";
968+
var experimentKey = "test_experiment";
969+
970+
Assert.False(Optimizely.SetForcedVariation(experimentKey, userId, "variation_not_in_datafile"));
971+
Assert.False(Optimizely.SetForcedVariation(experimentKey, userId, ""));
972+
}
973+
946974
// check that the get forced variation is correct.
947975
[Test]
948976
public void TestGetForcedVariation()
@@ -986,6 +1014,28 @@ public void TestGetForcedVariation()
9861014
Assert.IsTrue(TestData.CompareObjects(expectedForcedVariation, actualForcedVariation));
9871015
}
9881016

1017+
[Test]
1018+
public void TestGetForcedVariationWithInvalidUserID()
1019+
{
1020+
var experimentKey = "test_experiment";
1021+
Optimizely.SetForcedVariation(experimentKey, "test_user", "test_variation");
1022+
1023+
Assert.Null(Optimizely.GetForcedVariation(experimentKey, null));
1024+
Assert.Null(Optimizely.GetForcedVariation(experimentKey, "invalid_user"));
1025+
}
1026+
1027+
[Test]
1028+
public void TestGetForcedVariationWithInvalidExperimentKey()
1029+
{
1030+
var userId = "test_user";
1031+
var experimentKey = "test_experiment";
1032+
Optimizely.SetForcedVariation(experimentKey, userId, "test_variation");
1033+
1034+
Assert.Null(Optimizely.GetForcedVariation("test_experiment", userId));
1035+
Assert.Null(Optimizely.GetForcedVariation("", userId));
1036+
Assert.Null(Optimizely.GetForcedVariation(null, userId));
1037+
}
1038+
9891039
[Test]
9901040
public void TestGetVariationAudienceMatchAfterSetForcedVariation()
9911041
{
@@ -1325,9 +1375,9 @@ public void TestGetFeatureVariableValueForTypeGivenNullOrEmptyArguments()
13251375
Assert.IsNull(Optimizely.GetFeatureVariableValueForType(featureKey, variableKey, null, null, variableType));
13261376
Assert.IsNull(Optimizely.GetFeatureVariableValueForType(featureKey, variableKey, "", null, variableType));
13271377

1328-
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Feature flag key must not be empty."), Times.Exactly(2));
1329-
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Variable key must not be empty."), Times.Exactly(2));
1330-
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "User ID must not be empty."), Times.Exactly(2));
1378+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Feature Key is in invalid format."), Times.Exactly(2));
1379+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Variable Key is in invalid format."), Times.Exactly(2));
1380+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Exactly(1));
13311381
}
13321382

13331383
// Should return null and log error message when feature key or variable key does not get found.
@@ -1487,8 +1537,8 @@ public void TestIsFeatureEnabledGivenNullOrEmptyArguments()
14871537
Assert.IsFalse(Optimizely.IsFeatureEnabled(null, TestUserId, null));
14881538
Assert.IsFalse(Optimizely.IsFeatureEnabled("", TestUserId, null));
14891539

1490-
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "User ID must not be empty."), Times.Exactly(2));
1491-
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Feature flag key must not be empty."), Times.Exactly(2));
1540+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided Feature Key is in invalid format."), Times.Exactly(2));
1541+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Provided User Id is in invalid format."), Times.Exactly(1));
14921542
}
14931543

14941544
// Should return false and log error message when feature flag key is not found in the datafile.
@@ -1941,5 +1991,48 @@ public void TestTrackValidateInputValues()
19411991
}
19421992

19431993
#endregion // Test ValidateStringInputs
1994+
1995+
#region TestValidateStringInputs
1996+
1997+
[Test]
1998+
public void TestValidateStringInputsWithValidValues()
1999+
{
2000+
var optly = Helper.CreatePrivateOptimizely();
2001+
2002+
bool result = (bool)optly.Invoke("ValidateStringInputs", new Dictionary<string, string> { { Optimizely.EXPERIMENT_KEY, "test_experiment" } });
2003+
Assert.True(result);
2004+
2005+
result = (bool)optly.Invoke("ValidateStringInputs", new Dictionary<string, string> { { Optimizely.EVENT_KEY, "buy_now_event" } });
2006+
Assert.True(result);
2007+
}
2008+
2009+
[Test]
2010+
public void TestValidateStringInputsWithInvalidValues()
2011+
{
2012+
var optly = Helper.CreatePrivateOptimizely();
2013+
2014+
bool result = (bool)optly.Invoke("ValidateStringInputs", new Dictionary<string, string> { { Optimizely.EXPERIMENT_KEY, "" } });
2015+
Assert.False(result);
2016+
2017+
result = (bool)optly.Invoke("ValidateStringInputs", new Dictionary<string, string> { { Optimizely.EVENT_KEY, null } });
2018+
Assert.False(result);
2019+
}
2020+
2021+
[Test]
2022+
public void TestValidateStringInputsWithUserId()
2023+
{
2024+
var optly = Helper.CreatePrivateOptimizely();
2025+
2026+
bool result = (bool)optly.Invoke("ValidateStringInputs", new Dictionary<string, string> { { Optimizely.USER_ID, "testUser" } });
2027+
Assert.True(result);
2028+
2029+
result = (bool)optly.Invoke("ValidateStringInputs", new Dictionary<string, string> { { Optimizely.USER_ID, "" } });
2030+
Assert.True(result);
2031+
2032+
result = (bool)optly.Invoke("ValidateStringInputs", new Dictionary<string, string> { { Optimizely.USER_ID, null } });
2033+
Assert.False(result);
2034+
}
2035+
2036+
#endregion //TestValidateStringInputs
19442037
}
19452038
}

OptimizelySDK.Tests/ProjectConfigTest.cs

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -723,65 +723,6 @@ public void TestGetForcedVariationLogs()
723723
LoggerMock.Verify(l => l.Log(LogLevel.DEBUG, string.Format(@"Variation ""{0}"" is mapped to experiment ""{1}"" and user ""{2}"" in the forced variation map", variationKey, experimentKey, userId)));
724724
}
725725

726-
[Test]
727-
public void TestGetForcedVariationWithInvalidUserID()
728-
{
729-
var experimentKey = "test_experiment";
730-
731-
Config.SetForcedVariation(experimentKey, "test_user", "test_variation");
732-
733-
Assert.Null(Config.GetForcedVariation(experimentKey, null));
734-
Assert.Null(Config.GetForcedVariation(experimentKey, ""));
735-
Assert.Null(Config.GetForcedVariation(experimentKey, "invalid_user"));
736-
}
737-
738-
[Test]
739-
public void TestGetForcedVariationWithInvalidExperimentKey()
740-
{
741-
var userId = "test_user";
742-
var experimentKey = "test_experiment";
743-
744-
Config.SetForcedVariation(experimentKey, userId, "test_variation");
745-
746-
747-
748-
Assert.Null(Config.GetForcedVariation("test_experiment", userId));
749-
Assert.Null(Config.GetForcedVariation("", userId));
750-
Assert.Null(Config.GetForcedVariation(null, userId));
751-
}
752-
753-
[Test]
754-
public void TestSetForcedVariationWithInvalidUserID()
755-
{
756-
var experimentKey = "test_experiment";
757-
var variation = "variation";
758-
759-
Assert.False(Config.SetForcedVariation(experimentKey, null, variation));
760-
Assert.False(Config.SetForcedVariation(experimentKey, "", variation));
761-
}
762-
763-
[Test]
764-
public void TestSetForcedVariationWithInvalidExperimentKey()
765-
{
766-
var userId = "test_user";
767-
var variation = "variation";
768-
769-
Assert.False(Config.SetForcedVariation("test_experiment_not_in_datafile", userId, variation));
770-
Assert.False(Config.SetForcedVariation("", userId, variation));
771-
Assert.False(Config.SetForcedVariation(null, userId, variation));
772-
}
773-
774-
[Test]
775-
public void TestSetForcedVariationWithInvalidVariationKey()
776-
{
777-
var userId = "test_user";
778-
var experimentKey = "test_experiment";
779-
780-
Assert.False(Config.SetForcedVariation(experimentKey, userId, "variation_not_in_datafile"));
781-
Assert.False(Config.SetForcedVariation(experimentKey, userId, ""));
782-
Assert.True(Config.SetForcedVariation(experimentKey, userId, null));
783-
}
784-
785726
[Test]
786727
public void TestSetForcedVariationMultipleSets()
787728
{

OptimizelySDK/Optimizely.cs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public static String SDK_TYPE {
7575
public const string USER_ID = "User Id";
7676
public const string EXPERIMENT_KEY = "Experiment Key";
7777
public const string EVENT_KEY = "Event Key";
78+
public const string FEATURE_KEY = "Feature Key";
79+
public const string VARIABLE_KEY = "Variable Key";
7880

7981
/// <summary>
8082
/// Optimizely constructor for managing Full Stack .NET projects.
@@ -337,7 +339,13 @@ public Variation GetVariation(string experimentKey, string userId, UserAttribute
337339
/// <returns>A boolean value that indicates if the set completed successfully.</returns>
338340
public bool SetForcedVariation(string experimentKey, string userId, string variationKey)
339341
{
340-
return Config.SetForcedVariation(experimentKey, userId, variationKey);
342+
var inputValues = new Dictionary<string, string>
343+
{
344+
{ USER_ID, userId },
345+
{ EXPERIMENT_KEY, experimentKey }
346+
};
347+
348+
return ValidateStringInputs(inputValues) && Config.SetForcedVariation(experimentKey, userId, variationKey);
341349
}
342350

343351
/// <summary>
@@ -348,9 +356,16 @@ public bool SetForcedVariation(string experimentKey, string userId, string varia
348356
/// <returns>null|string The variation key.</returns>
349357
public Variation GetForcedVariation(string experimentKey, string userId)
350358
{
351-
var forcedVariation = Config.GetForcedVariation(experimentKey, userId);
359+
var inputValues = new Dictionary<string, string>
360+
{
361+
{ USER_ID, userId },
362+
{ EXPERIMENT_KEY, experimentKey }
363+
};
352364

353-
return forcedVariation;
365+
if (!ValidateStringInputs(inputValues))
366+
return null;
367+
368+
return Config.GetForcedVariation(experimentKey, userId);
354369
}
355370

356371
#region FeatureFlag APIs
@@ -365,15 +380,14 @@ public Variation GetForcedVariation(string experimentKey, string userId)
365380
/// <returns>True if feature is enabled, false or null otherwise</returns>
366381
public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttributes userAttributes = null)
367382
{
368-
if (string.IsNullOrEmpty(userId)) {
369-
Logger.Log(LogLevel.ERROR, "User ID must not be empty.");
370-
return false;
371-
}
383+
var inputValues = new Dictionary<string, string>
384+
{
385+
{ USER_ID, userId },
386+
{ FEATURE_KEY, featureKey }
387+
};
372388

373-
if (string.IsNullOrEmpty(featureKey)) {
374-
Logger.Log(LogLevel.ERROR, "Feature flag key must not be empty.");
389+
if (!ValidateStringInputs(inputValues))
375390
return false;
376-
}
377391

378392
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
379393
if (string.IsNullOrEmpty(featureFlag.Key))
@@ -395,7 +409,6 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
395409
}
396410
}
397411

398-
399412
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is not enabled for user ""{userId}"".");
400413
return false;
401414
}
@@ -412,23 +425,15 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
412425
public virtual string GetFeatureVariableValueForType(string featureKey, string variableKey, string userId,
413426
UserAttributes userAttributes, FeatureVariable.VariableType variableType)
414427
{
415-
if (string.IsNullOrEmpty(featureKey))
416-
{
417-
Logger.Log(LogLevel.ERROR, "Feature flag key must not be empty.");
418-
return null;
419-
}
420-
421-
if (string.IsNullOrEmpty(variableKey))
428+
var inputValues = new Dictionary<string, string>
422429
{
423-
Logger.Log(LogLevel.ERROR, "Variable key must not be empty.");
424-
return null;
425-
}
430+
{ USER_ID, userId },
431+
{ FEATURE_KEY, featureKey },
432+
{ VARIABLE_KEY, variableKey }
433+
};
426434

427-
if (string.IsNullOrEmpty(userId))
428-
{
429-
Logger.Log(LogLevel.ERROR, "User ID must not be empty.");
435+
if (!ValidateStringInputs(inputValues))
430436
return null;
431-
}
432437

433438
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
434439
if (string.IsNullOrEmpty(featureFlag.Key))
@@ -614,6 +619,9 @@ public List<string> GetEnabledFeatures(string userId, UserAttributes userAttribu
614619
return enabledFeaturesList;
615620
}
616621

622+
if (!ValidateStringInputs(new Dictionary<string, string> { { USER_ID, userId } }))
623+
return enabledFeaturesList;
624+
617625
foreach (var feature in Config.FeatureKeyMap.Values)
618626
{
619627
var featureKey = feature.Key;
@@ -634,6 +642,19 @@ public List<string> GetEnabledFeatures(string userId, UserAttributes userAttribu
634642
private bool ValidateStringInputs(Dictionary<string, string> inputs)
635643
{
636644
bool isValid = true;
645+
646+
// Empty user Id is valid value.
647+
if (inputs.ContainsKey(USER_ID))
648+
{
649+
if (inputs[USER_ID] == null)
650+
{
651+
Logger.Log(LogLevel.ERROR, $"Provided {USER_ID} is in invalid format.");
652+
isValid = false;
653+
}
654+
655+
inputs.Remove(USER_ID);
656+
}
657+
637658
foreach(var input in inputs)
638659
{
639660
if (string.IsNullOrEmpty(input.Value))

OptimizelySDK/ProjectConfig.cs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -458,12 +458,6 @@ public Variation GetVariationFromId(string experimentKey, string variationId)
458458
/// <returns>Variation entity which the given user and experiment should be forced into.</returns>
459459
public Variation GetForcedVariation(string experimentKey, string userId)
460460
{
461-
if (string.IsNullOrEmpty(userId))
462-
{
463-
Logger.Log(LogLevel.DEBUG, "User ID is invalid.");
464-
return null;
465-
}
466-
467461
if (_ForcedVariationMap.ContainsKey(userId) == false)
468462
{
469463
Logger.Log(LogLevel.DEBUG, string.Format(@"User ""{0}"" is not in the forced variation map.", userId));
@@ -514,19 +508,7 @@ public Variation GetForcedVariation(string experimentKey, string userId)
514508
/// <returns>A boolean value that indicates if the set completed successfully.</returns>
515509
public bool SetForcedVariation(string experimentKey, string userId, string variationKey)
516510
{
517-
if (string.IsNullOrEmpty(userId))
518-
{
519-
Logger.Log(LogLevel.DEBUG, "User ID is invalid.");
520-
return false;
521-
}
522-
523-
if (string.IsNullOrEmpty(experimentKey))
524-
{
525-
Logger.Log(LogLevel.DEBUG, "Experiment key is invalid.");
526-
return false;
527-
}
528-
529-
// Empty variation key is invalid.
511+
// Empty variation key is considered as invalid.
530512
if (variationKey != null && variationKey.Length == 0)
531513
{
532514
Logger.Log(LogLevel.DEBUG, "Variation key is invalid.");

0 commit comments

Comments
 (0)