Skip to content

Commit 4117938

Browse files
mfahadahmedaliabbasrizvi
authored andcommitted
feat(api): Decision Notification Listener for IsFeatureEnabled and GetEnabledFeatures APIs. (#149)
1 parent c0dc13c commit 4117938

File tree

9 files changed

+363
-31
lines changed

9 files changed

+363
-31
lines changed

OptimizelySDK.Tests/DecisionServiceTest.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -819,12 +819,13 @@ public void TestGetVariationForFeatureWhenTheUserIsNotBucketedIntoFeatureExperim
819819
public void TestGetVariationForFeatureWhenTheUserIsNeitherBucketedIntoFeatureExperimentNorToFeatureRollout()
820820
{
821821
var featureFlag = ProjectConfig.GetFeatureFlagFromKey("string_single_variable_feature");
822+
var expectedDecision = new FeatureDecision(null, null, FeatureDecision.DECISION_SOURCE_ROLLOUT);
822823

823824
DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny<FeatureFlag>(), It.IsAny<string>(), It.IsAny<UserAttributes>())).Returns<Variation>(null);
824825
DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureRollout(It.IsAny<FeatureFlag>(), It.IsAny<string>(), It.IsAny<UserAttributes>())).Returns<Variation>(null);
825826

826827
var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", new UserAttributes());
827-
Assert.IsNull(actualDecision);
828+
Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
828829

829830
LoggerMock.Verify(l => l.Log(LogLevel.INFO, "The user \"user1\" is not bucketed into a rollout for feature flag \"string_single_variable_feature\"."));
830831
}

OptimizelySDK.Tests/NotificationTests/NotificationCenterTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ public virtual void TestTrackCallback(string eventKey, string userId, UserAttrib
236236
public virtual void TestAnotherTrackCallback(string eventKey, string userId, UserAttributes userAttributes,
237237
EventTags eventTags, LogEvent logEvent) {
238238
}
239-
240-
public virtual void TestDecisionCallback(string type, string userId, UserAttributes userAttributes,
239+
240+
public virtual void TestDecisionCallback(string type, string userId, UserAttributes userAttributes,
241241
Dictionary<string, object> decisionInfo) {
242242
}
243243
}

OptimizelySDK.Tests/OptimizelyTest.cs

+278-7
Large diffs are not rendered by default.

OptimizelySDK.Tests/ProjectConfigTest.cs

+10
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ public void TestInit()
221221
{"177784", Config.GetVariationFromKey("177783", "177784") }
222222
}
223223
},
224+
{ "188880", new Dictionary<string, object>
225+
{
226+
{"188881", Config.GetVariationFromKey("188880", "188881") }
227+
}
228+
},
224229
{ "etag1", new Dictionary<string, object>
225230
{
226231
{"vtag1", Config.GetVariationFromKey("etag1", "vtag1") },
@@ -337,6 +342,11 @@ public void TestInit()
337342
{"177784", Config.GetVariationFromId("177783", "177784") }
338343
}
339344
},
345+
{ "188880", new Dictionary<string, object>
346+
{
347+
{"188881", Config.GetVariationFromId("188880", "188881") }
348+
}
349+
},
340350
{ "etag1", new Dictionary<string, object>
341351
{
342352
{"276", Config.GetVariationFromId("etag1", "276") },

OptimizelySDK.Tests/TestData.json

+26
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,32 @@
748748
"endOfRange": 9000
749749
}
750750
]
751+
},
752+
{
753+
"id": "188880",
754+
"key": "188880",
755+
"status": "Running",
756+
"layerId": "166660",
757+
"audienceIds": [],
758+
"variations": [
759+
{
760+
"id": "188881",
761+
"key": "188881",
762+
"variables": [
763+
{
764+
"id": "155556",
765+
"value": "false"
766+
}
767+
],
768+
"featureEnabled": false
769+
}
770+
],
771+
"trafficAllocation": [
772+
{
773+
"entityId": "188881",
774+
"endOfRange": 9200
775+
}
776+
]
751777
}
752778
]
753779
},

OptimizelySDK/Bucketing/DecisionService.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,13 @@ public virtual FeatureDecision GetVariationForFeature(FeatureFlag featureFlag, s
381381
decision = GetVariationForFeatureRollout(featureFlag, userId, filteredAttributes);
382382

383383
if (decision != null)
384+
{
384385
Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is bucketed into a rollout for feature flag \"{featureFlag.Key}\".");
385-
else
386-
Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is not bucketed into a rollout for feature flag \"{featureFlag.Key}\".");
386+
return decision;
387+
}
387388

388-
return decision;
389+
Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is not bucketed into a rollout for feature flag \"{featureFlag.Key}\".");
390+
return new FeatureDecision(null, null, FeatureDecision.DECISION_SOURCE_ROLLOUT);
389391
}
390392

391393
/// <summary>

OptimizelySDK/Entity/FeatureDecision.cs

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017, Optimizely
2+
* Copyright 2017, 2019, Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,20 +14,17 @@
1414
* limitations under the License.
1515
*/
1616

17-
using System;
18-
using System.Collections.Generic;
19-
2017
namespace OptimizelySDK.Entity
2118
{
2219
public class FeatureDecision
2320
{
24-
public const string DECISION_SOURCE_EXPERIMENT = "experiment";
25-
public const string DECISION_SOURCE_ROLLOUT = "rollout";
21+
public const string DECISION_SOURCE_EXPERIMENT = "EXPERIMENT";
22+
public const string DECISION_SOURCE_ROLLOUT = "ROLLOUT";
2623

2724
public Experiment Experiment { get; }
2825
public Variation Variation { get; }
2926
public string Source { get; }
30-
27+
3128
public FeatureDecision(Experiment experiment, Variation variation, string source)
3229
{
3330
Experiment = experiment;

OptimizelySDK/Optimizely.cs

+35-11
Original file line numberDiff line numberDiff line change
@@ -380,21 +380,45 @@ public virtual bool IsFeatureEnabled(string featureKey, string userId, UserAttri
380380
if (!Validator.IsFeatureFlagValid(Config, featureFlag))
381381
return false;
382382

383+
string experimentKey = null;
384+
string variationKey = null;
385+
bool featureEnabled = false;
383386
var decision = DecisionService.GetVariationForFeature(featureFlag, userId, userAttributes);
384-
if (decision != null) {
385-
if (decision.Source == FeatureDecision.DECISION_SOURCE_EXPERIMENT) {
386-
SendImpressionEvent(decision.Experiment, decision.Variation, userId, userAttributes);
387-
} else {
388-
Logger.Log(LogLevel.INFO, $@"The user ""{userId}"" is not being experimented on feature ""{featureKey}"".");
387+
388+
if (decision.Variation != null)
389+
{
390+
var variation = decision.Variation;
391+
featureEnabled = variation.FeatureEnabled.GetValueOrDefault();
392+
393+
if (decision.Source == FeatureDecision.DECISION_SOURCE_EXPERIMENT)
394+
{
395+
experimentKey = decision.Experiment.Key;
396+
variationKey = variation.Key;
397+
SendImpressionEvent(decision.Experiment, variation, userId, userAttributes);
389398
}
390-
if (decision.Variation.IsFeatureEnabled) {
391-
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is enabled for user ""{userId}"".");
392-
return true;
399+
else
400+
{
401+
Logger.Log(LogLevel.INFO, $@"The user ""{userId}"" is not being experimented on feature ""{featureKey}"".");
393402
}
394403
}
395404

396-
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is not enabled for user ""{userId}"".");
397-
return false;
405+
if (featureEnabled == true)
406+
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is enabled for user ""{userId}"".");
407+
else
408+
Logger.Log(LogLevel.INFO, $@"Feature flag ""{featureKey}"" is not enabled for user ""{userId}"".");
409+
410+
var decisionInfo = new Dictionary<string, object>
411+
{
412+
{ "featureKey", featureKey },
413+
{ "featureEnabled", featureEnabled },
414+
{ "source", decision.Source },
415+
{ "sourceExperimentKey", experimentKey },
416+
{ "sourceVariationKey", variationKey },
417+
};
418+
419+
NotificationCenter.SendNotifications(NotificationCenter.NotificationType.Decision, DecisionInfoTypes.FEATURE, userId,
420+
userAttributes ?? new UserAttributes(), decisionInfo);
421+
return featureEnabled;
398422
}
399423

400424
/// <summary>
@@ -440,7 +464,7 @@ public virtual string GetFeatureVariableValueForType(string featureKey, string v
440464
var variableValue = featureVariable.DefaultValue;
441465
var decision = DecisionService.GetVariationForFeature(featureFlag, userId, userAttributes);
442466

443-
if (decision != null)
467+
if (decision.Variation != null)
444468
{
445469
var variation = decision.Variation;
446470
var featureVariableUsageInstance = variation.GetFeatureVariableUsageFromId(featureVariable.Id);

OptimizelySDK/Utils/DecisionInfoTypes.cs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace OptimizelySDK.Utils
1818
{
1919
public static class DecisionInfoTypes
2020
{
21+
public const string FEATURE = "feature";
2122
public const string EXPERIMENT = "experiment";
2223
}
2324
}

0 commit comments

Comments
 (0)