Skip to content

Commit ae81fcf

Browse files
msohailhussainmikeproeng37
authored andcommitted
getvariationforfeature should return DecisionFeature entity (#42)
1 parent 32808d6 commit ae81fcf

File tree

9 files changed

+189
-115
lines changed

9 files changed

+189
-115
lines changed

OptimizelySDK.Net35/OptimizelySDK.Net35.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
</Compile>
6363
<Compile Include="..\OptimizelySDK\Entity\Experiment.cs">
6464
<Link>Entity\Experiment.cs</Link>
65+
</Compile>
66+
<Compile Include="..\OptimizelySDK\Entity\FeatureDecision.cs">
67+
<Link>Entity\FeatureDecision.cs</Link>
6568
</Compile>
6669
<Compile Include="..\OptimizelySDK\Entity\ForcedVariation.cs">
6770
<Link>Entity\ForcedVariation.cs</Link>

OptimizelySDK.Net40/OptimizelySDK.Net40.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@
6363
</Compile>
6464
<Compile Include="..\OptimizelySDK\Entity\Experiment.cs">
6565
<Link>Entity\Experiment.cs</Link>
66+
</Compile>
67+
<Compile Include="..\OptimizelySDK\Entity\FeatureDecision.cs">
68+
<Link>Entity\FeatureDecision.cs</Link>
6669
</Compile>
6770
<Compile Include="..\OptimizelySDK\Entity\ForcedVariation.cs">
6871
<Link>Entity\ForcedVariation.cs</Link>

OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<Compile Include="..\OptimizelySDK\Entity\Event.cs" />
1515
<Compile Include="..\OptimizelySDK\Entity\EventAttributes.cs" />
1616
<Compile Include="..\OptimizelySDK\Entity\Experiment.cs" />
17+
<Compile Include="..\OptimizelySDK\Entity\FeatureDecision.cs" />
1718
<Compile Include="..\OptimizelySDK\Entity\ForcedVariation.cs" />
1819
<Compile Include="..\OptimizelySDK\Entity\Group.cs" />
1920
<Compile Include="..\OptimizelySDK\Entity\IdKeyEntity.cs" />

OptimizelySDK.Tests/DecisionServiceTest.cs

Lines changed: 80 additions & 66 deletions
Large diffs are not rendered by default.

OptimizelySDK.Tests/OptimizelyTest.cs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,12 +1156,12 @@ public void TestGetFeatureVariableBooleanReturnTypecastedValue()
11561156
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableKeyNonBoolean, It.IsAny<string>(),
11571157
It.IsAny<UserAttributes>(), featureVariableType)).Returns("non_boolean_value");
11581158

1159-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableBoolean(featureKey, variableKeyNonBoolean, TestUserId, null));
1159+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableBoolean(featureKey, variableKeyNonBoolean, TestUserId, null));
11601160
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, $@"Unable to cast variable value ""non_boolean_value"" to type ""{featureVariableType}""."));
11611161

11621162
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableKeyNull, It.IsAny<string>(),
11631163
It.IsAny<UserAttributes>(), featureVariableType)).Returns<string>(null);
1164-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableBoolean(featureKey, variableKeyNull, TestUserId, null));
1164+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableBoolean(featureKey, variableKeyNull, TestUserId, null));
11651165
}
11661166

11671167
[Test]
@@ -1185,12 +1185,12 @@ public void TestGetFeatureVariableDoubleReturnTypecastedValue()
11851185
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableKeyNonDouble, It.IsAny<string>(),
11861186
It.IsAny<UserAttributes>(), featureVariableType)).Returns("non_double_value");
11871187

1188-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableDouble(featureKey, variableKeyNonDouble, TestUserId, null));
1188+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableDouble(featureKey, variableKeyNonDouble, TestUserId, null));
11891189
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, $@"Unable to cast variable value ""non_double_value"" to type ""{featureVariableType}""."));
11901190

11911191
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableKeyNull, It.IsAny<string>(),
11921192
It.IsAny<UserAttributes>(), featureVariableType)).Returns<string>(null);
1193-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableDouble(featureKey, variableKeyNull, TestUserId, null));
1193+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableDouble(featureKey, variableKeyNull, TestUserId, null));
11941194
}
11951195

11961196
[Test]
@@ -1210,18 +1210,18 @@ public void TestGetFeatureVariableIntegerReturnTypecastedValue()
12101210
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableKeyDouble, It.IsAny<string>(),
12111211
It.IsAny<UserAttributes>(), featureVariableType)).Returns("100.45");
12121212

1213-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableKeyDouble, TestUserId, null));
1213+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableKeyDouble, TestUserId, null));
12141214
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, $@"Unable to cast variable value ""100.45"" to type ""{featureVariableType}""."));
12151215

12161216
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableNonInt, It.IsAny<string>(),
12171217
It.IsAny<UserAttributes>(), featureVariableType)).Returns("non_integer_value");
12181218

1219-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableNonInt, TestUserId, null));
1219+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableNonInt, TestUserId, null));
12201220
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, $@"Unable to cast variable value ""non_integer_value"" to type ""{featureVariableType}""."));
12211221

12221222
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableKeyNull, It.IsAny<string>(),
12231223
It.IsAny<UserAttributes>(), featureVariableType)).Returns<string>(null);
1224-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableKeyNull, TestUserId, null));
1224+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableInteger(featureKey, variableKeyNull, TestUserId, null));
12251225
}
12261226

12271227
[Test]
@@ -1243,7 +1243,7 @@ public void TestGetFeatureVariableStringReturnTypecastedValue()
12431243

12441244
OptimizelyMock.Setup(om => om.GetFeatureVariableValueForType(It.IsAny<string>(), variableKeyNull, It.IsAny<string>(),
12451245
It.IsAny<UserAttributes>(), featureVariableType)).Returns<string>(null);
1246-
Assert.AreEqual(null, OptimizelyMock.Object.GetFeatureVariableString(featureKey, variableKeyNull, TestUserId, null));
1246+
Assert.Null(OptimizelyMock.Object.GetFeatureVariableString(featureKey, variableKeyNull, TestUserId, null));
12471247
}
12481248

12491249
#endregion // Test GetFeatureVariable<Type> TypeCasting
@@ -1325,7 +1325,7 @@ public void TestGetFeatureVariableValueForTypeGivenFeatureFlagIsNotEnabledForUse
13251325
var variableType = FeatureVariable.VariableType.DOUBLE;
13261326
var expectedValue = "14.99";
13271327

1328-
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns<Variation>(null);
1328+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns<FeatureDecision>(null);
13291329

13301330
var optly = Helper.CreatePrivateOptimizely();
13311331
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1344,13 +1344,15 @@ public void TestGetFeatureVariableValueForTypeGivenFeatureFlagIsEnabledForUserAn
13441344
{
13451345
var featureKey = "double_single_variable_feature";
13461346
var featureFlag = Config.GetFeatureFlagFromKey("double_single_variable_feature");
1347+
var experiment = Config.GetExperimentFromKey("test_experiment_integer_feature");
13471348
var differentVariation = Config.GetVariationFromKey("test_experiment_integer_feature", "control");
1349+
var expectedDecision = new FeatureDecision(experiment.Id, differentVariation.Id, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
13481350
var variableKey = "double_variable";
13491351
var variableType = FeatureVariable.VariableType.DOUBLE;
13501352
var expectedValue = "14.99";
13511353

13521354
// Mock GetVariationForFeature method to return variation of different feature.
1353-
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(differentVariation);
1355+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(expectedDecision);
13541356

13551357
var optly = Helper.CreatePrivateOptimizely();
13561358
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1372,9 +1374,11 @@ public void TestGetFeatureVariableValueForTypeGivenFeatureFlagIsEnabledForUserAn
13721374
var variableKey = "double_variable";
13731375
var variableType = FeatureVariable.VariableType.DOUBLE;
13741376
var expectedValue = "42.42";
1375-
1377+
var experiment = Config.GetExperimentFromKey("test_experiment_double_feature");
13761378
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "control");
1377-
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(variation);
1379+
var decision = new FeatureDecision(experiment.Id, variation.Id, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
1380+
1381+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
13781382

13791383
var optly = Helper.CreatePrivateOptimizely();
13801384
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1442,7 +1446,7 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsNotEnabledForUser()
14421446
var featureKey = "double_single_variable_feature";
14431447
var featureFlag = Config.GetFeatureFlagFromKey("double_single_variable_feature");
14441448

1445-
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns<Variation>(null);
1449+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns<FeatureDecision>(null);
14461450

14471451
var optly = Helper.CreatePrivateOptimizely();
14481452
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1464,8 +1468,9 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsEnabledAndUserIsNotBeingExperi
14641468
var experiment = rollout.Experiments[0];
14651469
var variation = experiment.Variations[0];
14661470
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
1471+
var decision = new FeatureDecision(experiment.Id, variation.Id, FeatureDecision.DECISION_SOURCE_ROLLOUT);
14671472

1468-
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(variation);
1473+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
14691474

14701475
var optly = Helper.CreatePrivateOptimizely();
14711476
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1487,10 +1492,12 @@ public void TestIsFeatureEnabledGivenFeatureFlagIsEnabledAndUserIsNotBeingExperi
14871492
public void TestIsFeatureEnabledGivenFeatureFlagIsEnabledAndUserIsBeingExperimented()
14881493
{
14891494
var featureKey = "double_single_variable_feature";
1495+
var experiment = Config.GetExperimentFromKey("test_experiment_double_feature");
14901496
var variation = Config.GetVariationFromKey("test_experiment_double_feature", "control");
14911497
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
1498+
var decision = new FeatureDecision(experiment.Id, variation.Id, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
14921499

1493-
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(variation);
1500+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, null)).Returns(decision);
14941501

14951502
var optly = Helper.CreatePrivateOptimizely();
14961503
optly.SetFieldOrProperty("DecisionService", DecisionServiceMock.Object);
@@ -1537,6 +1544,7 @@ public void TestActivateListener(UserAttributes userAttributes)
15371544
var experiment = Config.GetExperimentFromKey(experimentKey);
15381545
var variation = Config.GetVariationFromKey(experimentKey, variationKey);
15391546
var featureFlag = Config.GetFeatureFlagFromKey(featureKey);
1547+
var decision = new FeatureDecision(experiment.Id, variation.Id, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
15401548
var logEvent = new LogEvent("https://logx.optimizely.com/v1/events", OptimizelyHelper.SingleParameter,
15411549
"POST", new Dictionary<string, string> { });
15421550

@@ -1550,7 +1558,7 @@ public void TestActivateListener(UserAttributes userAttributes)
15501558
EventBuilderMock.Setup(ebm => ebm.CreateImpressionEvent(It.IsAny<ProjectConfig>(), It.IsAny<Experiment>(),
15511559
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<UserAttributes>())).Returns(logEvent);
15521560
DecisionServiceMock.Setup(ds => ds.GetVariation(experiment, TestUserId, userAttributes)).Returns(variation);
1553-
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(variation);
1561+
DecisionServiceMock.Setup(ds => ds.GetVariationForFeature(featureFlag, TestUserId, userAttributes)).Returns(decision);
15541562

15551563
var optly = Helper.CreatePrivateOptimizely();
15561564
var optStronglyTyped = optly.GetObject() as Optimizely;

OptimizelySDK/Bucketing/DecisionService.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,15 @@ public void SaveVariation(Experiment experiment, Variation variation, UserProfil
256256
/// <param name = "userId" >User Identifier</param>
257257
/// <param name = "filteredAttributes" >The user's attributes. This should be filtered to just attributes in the Datafile.</param>
258258
/// <returns>null if the user is not bucketed into the rollout or if the feature flag was not attached to a rollout.
259-
/// otherwise the Variation the user is bucketed into</returns>
260-
public virtual Variation GetVariationForFeatureRollout(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
259+
/// otherwise the FeatureDecision entity</returns>
260+
public virtual FeatureDecision GetVariationForFeatureRollout(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
261261
{
262+
if (featureFlag == null)
263+
{
264+
Logger.Log(LogLevel.ERROR, "Invalid feature flag provided.");
265+
return null;
266+
}
267+
262268
if (string.IsNullOrEmpty(featureFlag.RolloutId))
263269
{
264270
Logger.Log(LogLevel.INFO, $"The feature flag \"{featureFlag.Key}\" is not used in a rollout.");
@@ -296,7 +302,7 @@ public virtual Variation GetVariationForFeatureRollout(FeatureFlag featureFlag,
296302
break;
297303
}
298304

299-
return variation;
305+
return new FeatureDecision(rolloutRule.Id, variation.Id, FeatureDecision.DECISION_SOURCE_ROLLOUT);
300306
}
301307
else
302308
{
@@ -311,9 +317,10 @@ public virtual Variation GetVariationForFeatureRollout(FeatureFlag featureFlag,
311317
if (variation == null)
312318
{
313319
Logger.Log(LogLevel.DEBUG, $"User \"{userId}\" is excluded from \"Everyone Else\" rule for feature flag \"{featureFlag.Key}\".");
320+
return null;
314321
}
315322

316-
return variation;
323+
return new FeatureDecision(everyoneElseRolloutRule.Id, variation.Id, FeatureDecision.DECISION_SOURCE_ROLLOUT);
317324
}
318325

319326
/// <summary>
@@ -323,9 +330,15 @@ public virtual Variation GetVariationForFeatureRollout(FeatureFlag featureFlag,
323330
/// <param name = "userId" >User Identifier</param>
324331
/// <param name = "filteredAttributes" >The user's attributes. This should be filtered to just attributes in the Datafile.</param>
325332
/// <returns>null if the user is not bucketed into the rollout or if the feature flag was not attached to a rollout.
326-
/// otherwise the Variation the user is bucketed into</returns>
327-
public virtual Variation GetVariationForFeatureExperiment(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
333+
/// Otherwise the FeatureDecision entity</returns>
334+
public virtual FeatureDecision GetVariationForFeatureExperiment(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
328335
{
336+
if (featureFlag == null)
337+
{
338+
Logger.Log(LogLevel.ERROR, "Invalid feature flag provided.");
339+
return null;
340+
}
341+
329342
if (featureFlag.ExperimentIds == null || featureFlag.ExperimentIds.Count == 0)
330343
{
331344
Logger.Log(LogLevel.INFO, $"The feature flag \"{featureFlag.Key}\" is not used in any experiments.");
@@ -344,7 +357,7 @@ public virtual Variation GetVariationForFeatureExperiment(FeatureFlag featureFla
344357
if (variation != null)
345358
{
346359
Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is bucketed into experiment \"{experiment.Key}\" of feature \"{featureFlag.Key}\".");
347-
return variation;
360+
return new FeatureDecision(experimentId, variation.Id, FeatureDecision.DECISION_SOURCE_EXPERIMENT);
348361
}
349362
}
350363

@@ -358,25 +371,25 @@ public virtual Variation GetVariationForFeatureExperiment(FeatureFlag featureFla
358371
/// <param name = "featureFlag" >The feature flag the user wants to access.</param>
359372
/// <param name = "userId" >User Identifier</param>
360373
/// <param name = "filteredAttributes" >The user's attributes. This should be filtered to just attributes in the Datafile.</param>
361-
/// <returns>null if the user is not bucketed into any variation or the Variation the user is bucketed into
362-
/// if the user is successfully bucketed.</returns>
363-
public virtual Variation GetVariationForFeature(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
374+
/// <returns>null if the user is not bucketed into any variation or the FeatureDecision entity if the user is
375+
/// successfully bucketed.</returns>
376+
public virtual FeatureDecision GetVariationForFeature(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes)
364377
{
365378
// Check if the feature flag has an experiment and the user is bucketed into that experiment.
366-
var variation = GetVariationForFeatureExperiment(featureFlag, userId, filteredAttributes);
379+
var decision = GetVariationForFeatureExperiment(featureFlag, userId, filteredAttributes);
367380

368-
if (variation != null)
369-
return variation;
381+
if (decision != null)
382+
return decision;
370383

371384
// Check if the feature flag has rollout and the the user is bucketed into one of its rules.
372-
variation = GetVariationForFeatureRollout(featureFlag, userId, filteredAttributes);
385+
decision = GetVariationForFeatureRollout(featureFlag, userId, filteredAttributes);
373386

374-
if (variation != null)
387+
if (decision != null)
375388
Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is bucketed into a rollout for feature flag \"{featureFlag.Key}\".");
376389
else
377390
Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is not bucketed into a rollout for feature flag \"{featureFlag.Key}\".");
378391

379-
return variation;
392+
return decision;
380393
}
381394

382395
/// <summary>

0 commit comments

Comments
 (0)