public void TestRemoveAllForcedDecisionsRemovesDecisions()
        {
            var user = Optimizely.CreateUserContext(UserID);

            var context1 = new OptimizelyDecisionContext("flagKey", "ruleKey1");
            var context2 = new OptimizelyDecisionContext("flagKey2", "ruleKey2");
            var context3 = new OptimizelyDecisionContext("flagKey3", "ruleKey3");

            var decision1 = new OptimizelyForcedDecision("variation");
            var decision2 = new OptimizelyForcedDecision("variation2");
            var decision3 = new OptimizelyForcedDecision("variation3");

            user.SetForcedDecision(context1, decision1);
            user.SetForcedDecision(context2, decision2);
            user.SetForcedDecision(context3, decision3);

            user.RemoveAllForcedDecisions();

            var result1 = user.GetForcedDecision(context1);

            Assert.AreEqual(null, result1);

            var result2 = user.GetForcedDecision(context2);

            Assert.AreEqual(null, result2);

            var result3 = user.GetForcedDecision(context3);

            Assert.AreEqual(null, result3);
        }
Esempio n. 2
0
        /// <summary>
        /// Get the variation if the user is bucketed for one of the experiments on this feature flag.
        /// </summary>
        /// <param name = "featureFlag" >The feature flag the user wants to access.</param>
        /// <param name = "userId" >User Identifier</param>
        /// <param name = "filteredAttributes" >The user's attributes. This should be filtered to just attributes in the Datafile.</param>
        /// <returns>null if the user is not bucketed into the rollout or if the feature flag was not attached to a rollout.
        /// Otherwise the FeatureDecision entity</returns>
        public virtual Result <FeatureDecision> GetVariationForFeatureExperiment(FeatureFlag featureFlag,
                                                                                 OptimizelyUserContext user,
                                                                                 UserAttributes filteredAttributes,
                                                                                 ProjectConfig config,
                                                                                 OptimizelyDecideOption[] options)
        {
            var reasons = new DecisionReasons();
            var userId  = user.GetUserId();

            if (featureFlag == null)
            {
                Logger.Log(LogLevel.ERROR, "Invalid feature flag provided.");
                return(Result <FeatureDecision> .NullResult(reasons));
            }

            if (featureFlag.ExperimentIds == null || featureFlag.ExperimentIds.Count == 0)
            {
                Logger.Log(LogLevel.INFO, reasons.AddInfo($"The feature flag \"{featureFlag.Key}\" is not used in any experiments."));
                return(Result <FeatureDecision> .NullResult(reasons));
            }

            foreach (var experimentId in featureFlag.ExperimentIds)
            {
                var       experiment        = config.GetExperimentFromId(experimentId);
                Variation decisionVariation = null;

                if (string.IsNullOrEmpty(experiment.Key))
                {
                    continue;
                }
                var decisionContext        = new OptimizelyDecisionContext(featureFlag.Key, experiment?.Key);
                var forcedDecisionResponse = ValidatedForcedDecision(decisionContext, config, user);

                reasons += forcedDecisionResponse.DecisionReasons;

                if (forcedDecisionResponse?.ResultObject != null)
                {
                    decisionVariation = forcedDecisionResponse.ResultObject;
                }
                else
                {
                    var decisionResponse = GetVariation(experiment, user, config, options);

                    reasons          += decisionResponse?.DecisionReasons;
                    decisionVariation = decisionResponse.ResultObject;
                }

                if (!string.IsNullOrEmpty(decisionVariation?.Id))
                {
                    Logger.Log(LogLevel.INFO, reasons.AddInfo($"The user \"{userId}\" is bucketed into experiment \"{experiment.Key}\" of feature \"{featureFlag.Key}\"."));

                    var featureDecision = new FeatureDecision(experiment, decisionVariation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST);
                    return(Result <FeatureDecision> .NewResult(featureDecision, reasons));
                }
            }

            Logger.Log(LogLevel.INFO, reasons.AddInfo($"The user \"{userId}\" is not bucketed into any of the experiments on the feature \"{featureFlag.Key}\"."));
            return(Result <FeatureDecision> .NullResult(reasons));
        }
        public void TestSetForcedDecisionSetsValue()
        {
            var user     = Optimizely.CreateUserContext(UserID);
            var context  = new OptimizelyDecisionContext("flag", null);
            var decision = new OptimizelyForcedDecision("variationKey");
            var result   = user.SetForcedDecision(context, decision);

            Assert.IsTrue(result);
        }
Esempio n. 4
0
        public void ForcedDecisionStoreNullContextForcedDecisionContext()
        {
            var expectedForcedDecision        = new OptimizelyForcedDecision("sample_variation_key");
            OptimizelyDecisionContext context = null;
            var forcedDecisionStore           = new ForcedDecisionsStore();

            forcedDecisionStore[context] = expectedForcedDecision;

            Assert.AreEqual(forcedDecisionStore.Count, 0);
        }
        public void TestGetForcedDecisionReturnsValueWithRuleKey()
        {
            var user     = Optimizely.CreateUserContext(UserID);
            var context  = new OptimizelyDecisionContext("flagKey", "ruleKey");
            var decision = new OptimizelyForcedDecision("variationKey");

            user.SetForcedDecision(context, decision);

            var result = user.GetForcedDecision(context);

            Assertions.AreEqual(decision, result);
        }
        public void TestGetForcedDecisionReturnsNullIfInvalidConfig()
        {
            var optly = new Optimizely(new FallbackProjectConfigManager(null));

            var user = optly.CreateUserContext(UserID);

            var context  = new OptimizelyDecisionContext("flag", null);
            var decision = new OptimizelyForcedDecision("variationKey");
            var result   = user.GetForcedDecision(context);

            Assert.IsNull(result);
        }
Esempio n. 7
0
        public void ForcedDecisionStoreRemoveAllForcedDecisionContext()
        {
            var expectedForcedDecision = new OptimizelyForcedDecision("sample_variation_key");
            var context             = new OptimizelyDecisionContext("flag_key", "rule_key");
            var forcedDecisionStore = new ForcedDecisionsStore();

            forcedDecisionStore[context] = expectedForcedDecision;

            Assert.AreEqual(forcedDecisionStore.Count, 1);
            forcedDecisionStore.RemoveAll();
            Assert.AreEqual(forcedDecisionStore.Count, 0);
        }
        public void TestForcedDecisionReturnsCorrectFlagAndRuleKeys()
        {
            var user    = Optimizely.CreateUserContext(UserID);
            var context = new OptimizelyDecisionContext("flag", null);

            Assert.AreEqual("flag", context.FlagKey);
            Assert.Null(context.RuleKey);

            context = new OptimizelyDecisionContext("flag", "ruleKey");

            Assert.AreEqual("flag", context.FlagKey);
            Assert.AreEqual("ruleKey", context.RuleKey);
        }
Esempio n. 9
0
        public void ForcedDecisionStoreGetForcedDecisionWithBothRuleAndFlagKey()
        {
            var expectedForcedDecision1 = new OptimizelyForcedDecision("sample_variation_key");
            var context1            = new OptimizelyDecisionContext("flag_key", "rule_key");
            var NullFlagKeyContext  = new OptimizelyDecisionContext(null, "rule_key");
            var forcedDecisionStore = new ForcedDecisionsStore();

            forcedDecisionStore[context1] = expectedForcedDecision1;

            Assert.AreEqual(forcedDecisionStore.Count, 1);
            Assert.AreEqual(forcedDecisionStore[context1].VariationKey, expectedForcedDecision1.VariationKey);
            Assert.IsNull(forcedDecisionStore[NullFlagKeyContext]);
        }
Esempio n. 10
0
        public void ForcedDecisionStoreRemoveForcedDecisionContextRuleKeyNotMatched()
        {
            var expectedForcedDecision = new OptimizelyForcedDecision("sample_variation_key");
            var contextNotMatched      = new OptimizelyDecisionContext("flag_key", "");
            var context             = new OptimizelyDecisionContext("flag_key", "rule_key");
            var forcedDecisionStore = new ForcedDecisionsStore();

            forcedDecisionStore[context] = expectedForcedDecision;

            Assert.AreEqual(forcedDecisionStore.Count, 1);
            Assert.IsFalse(forcedDecisionStore.Remove(contextNotMatched));
            Assert.AreEqual(forcedDecisionStore.Count, 1);
        }
        public void TestRemoveForcedDecisionRemovesDecision()
        {
            var user      = Optimizely.CreateUserContext(UserID);
            var context   = new OptimizelyDecisionContext("flagKey", "ruleKey");
            var decision  = new OptimizelyForcedDecision("variationKey");
            var setResult = user.SetForcedDecision(context, decision);

            Assert.IsTrue(setResult);

            user.RemoveForcedDecision(context);

            var result = user.GetForcedDecision(context);

            Assert.AreEqual(null, result);
        }
        public void TestFindValidatedForcedDecisionReturnsCorrectDecisionWithNullVariation()
        {
            var decisionReasons = new DecisionReasons();

            decisionReasons.AddInfo("{0}", "Invalid variation is mapped to flag: flagKey and rule: rule forced decision map.");
            var expectedResult = Result <Variation> .NullResult(decisionReasons);

            var user = Optimizely.CreateUserContext(UserID);

            var context = new OptimizelyDecisionContext("flagKey", "ruleKey");

            var result = user.FindValidatedForcedDecision(context, null);

            Assertions.AreEqual(expectedResult, result);
        }
Esempio n. 13
0
        public void ForcedDecisionStoreGetSetForcedDecisionWithBothRuleAndFlagKey()
        {
            var expectedForcedDecision1 = new OptimizelyForcedDecision("sample_variation_key");
            var expectedForcedDecision2 = new OptimizelyForcedDecision("sample_variation_key_2");
            var context1            = new OptimizelyDecisionContext("flag_key", "rule_key");
            var context2            = new OptimizelyDecisionContext("flag_key", "rule_key1");
            var forcedDecisionStore = new ForcedDecisionsStore();

            forcedDecisionStore[context1] = expectedForcedDecision1;
            forcedDecisionStore[context2] = expectedForcedDecision2;

            Assert.AreEqual(forcedDecisionStore.Count, 2);
            Assert.AreEqual(forcedDecisionStore[context1].VariationKey, expectedForcedDecision1.VariationKey);
            Assert.AreEqual(forcedDecisionStore[context2].VariationKey, expectedForcedDecision2.VariationKey);
        }
Esempio n. 14
0
        public void ForcedDecisionStoreRemoveForcedDecisionTrue()
        {
            var expectedForcedDecision1 = new OptimizelyForcedDecision("sample_variation_key");
            var expectedForcedDecision2 = new OptimizelyForcedDecision("sample_variation_key_2");
            var context1            = new OptimizelyDecisionContext("flag_key", "rule_key");
            var context2            = new OptimizelyDecisionContext("flag_key", "rule_key1");
            var forcedDecisionStore = new ForcedDecisionsStore();

            forcedDecisionStore[context1] = expectedForcedDecision1;
            forcedDecisionStore[context2] = expectedForcedDecision2;

            Assert.AreEqual(forcedDecisionStore.Count, 2);
            Assert.IsTrue(forcedDecisionStore.Remove(context2));
            Assert.AreEqual(forcedDecisionStore.Count, 1);
            Assert.AreEqual(forcedDecisionStore[context1].VariationKey, expectedForcedDecision1.VariationKey);
            Assert.IsNull(forcedDecisionStore[context2]);
        }
Esempio n. 15
0
        /// <summary>
        /// Finds a validated forced decision.
        /// </summary>
        /// <param name="context">Object containing flag and rule key of which forced decision is set.</param>
        /// <param name="config">The Project config.</param>
        /// <param name="user">Optimizely user context.</param>
        /// <returns>A result with the variation</returns>
        public Result <Variation> ValidatedForcedDecision(OptimizelyDecisionContext context, ProjectConfig config, OptimizelyUserContext user)
        {
            DecisionReasons reasons        = new DecisionReasons();
            var             userId         = user.GetUserId();
            var             forcedDecision = user.GetForcedDecision(context);

            if (config != null && forcedDecision != null)
            {
                var loggingKey   = context.RuleKey != null ? "flag (" + context.FlagKey + "), rule (" + context.RuleKey + ")" : "flag (" + context.FlagKey + ")";
                var variationKey = forcedDecision.VariationKey;
                var variation    = config.GetFlagVariationByKey(context.FlagKey, variationKey);
                if (variation != null)
                {
                    reasons.AddInfo("Decided by forced decision.");
                    reasons.AddInfo("Variation ({0}) is mapped to {1} and user ({2}) in the forced decision map.", variationKey, loggingKey, userId);
                    return(Result <Variation> .NewResult(variation, reasons));
                }
                else
                {
                    reasons.AddInfo("Invalid variation is mapped to {0} and user ({1}) in the forced decision map.", loggingKey, userId);
                }
            }
            return(Result <Variation> .NullResult(reasons));
        }
Esempio n. 16
0
        /// <summary>
        /// Try to bucket the user into a rollout rule.
        /// Evaluate the user for rules in priority order by seeing if the user satisfies the audience.
        /// Fall back onto the everyone else rule if the user is ever excluded from a rule due to traffic allocation.
        /// </summary>
        /// <param name = "featureFlag" >The feature flag the user wants to access.</param>
        /// <param name = "userId" >User Identifier</param>
        /// <param name = "filteredAttributes" >The user's attributes. This should be filtered to just attributes in the Datafile.</param>
        /// <param name = "reasons" >Decision log messages.</param>
        /// <returns>null if the user is not bucketed into the rollout or if the feature flag was not attached to a rollout.
        /// otherwise the FeatureDecision entity</returns>
        public virtual Result <FeatureDecision> GetVariationForFeatureRollout(FeatureFlag featureFlag,
                                                                              OptimizelyUserContext user,
                                                                              ProjectConfig config)
        {
            var reasons = new DecisionReasons();

            if (featureFlag == null)
            {
                Logger.Log(LogLevel.ERROR, "Invalid feature flag provided.");
                return(Result <FeatureDecision> .NullResult(reasons));
            }

            if (string.IsNullOrEmpty(featureFlag.RolloutId))
            {
                Logger.Log(LogLevel.INFO, reasons.AddInfo($"The feature flag \"{featureFlag.Key}\" is not used in a rollout."));
                return(Result <FeatureDecision> .NullResult(reasons));
            }

            Rollout rollout = config.GetRolloutFromId(featureFlag.RolloutId);

            if (string.IsNullOrEmpty(rollout.Id))
            {
                Logger.Log(LogLevel.ERROR, reasons.AddInfo($"The rollout with id \"{featureFlag.RolloutId}\" is not found in the datafile for feature flag \"{featureFlag.Key}\""));
                return(Result <FeatureDecision> .NullResult(reasons));
            }

            if (rollout.Experiments == null || rollout.Experiments.Count == 0)
            {
                return(Result <FeatureDecision> .NullResult(reasons));
            }

            var rolloutRulesLength = rollout.Experiments.Count;
            var rolloutRules       = rollout.Experiments;

            var userId     = user.GetUserId();
            var attributes = user.GetAttributes();

            var index = 0;

            while (index < rolloutRulesLength)
            {
                // To skip rules
                var skipToEveryoneElse = false;

                //Check forced decision first
                var rule                   = rolloutRules[index];
                var decisionContext        = new OptimizelyDecisionContext(featureFlag.Key, rule.Key);
                var forcedDecisionResponse = ValidatedForcedDecision(decisionContext, config, user);

                reasons += forcedDecisionResponse.DecisionReasons;
                if (forcedDecisionResponse.ResultObject != null)
                {
                    return(Result <FeatureDecision> .NewResult(new FeatureDecision(rule, forcedDecisionResponse.ResultObject, null), reasons));
                }

                // Regular decision

                // Get Bucketing ID from user attributes.
                var bucketingIdResult = GetBucketingId(userId, attributes);
                reasons += bucketingIdResult.DecisionReasons;

                var everyoneElse = index == rolloutRulesLength - 1;

                var loggingKey = everyoneElse ? "Everyone Else" : string.Format("{0}", index + 1);

                // Evaluate if user meets the audience condition of this rollout rule
                var doesUserMeetAudienceConditionsResult = ExperimentUtils.DoesUserMeetAudienceConditions(config, rule, attributes, LOGGING_KEY_TYPE_RULE, rule.Key, Logger);
                reasons += doesUserMeetAudienceConditionsResult.DecisionReasons;
                if (doesUserMeetAudienceConditionsResult.ResultObject)
                {
                    Logger.Log(LogLevel.INFO, reasons.AddInfo($"User \"{userId}\" meets condition for targeting rule \"{loggingKey}\"."));

                    var bucketedVariation = Bucketer.Bucket(config, rule, bucketingIdResult.ResultObject, userId);
                    reasons += bucketedVariation?.DecisionReasons;

                    if (bucketedVariation?.ResultObject?.Key != null)
                    {
                        Logger.Log(LogLevel.INFO, reasons.AddInfo($"User \"{userId}\" is in the traffic group of targeting rule \"{loggingKey}\"."));

                        return(Result <FeatureDecision> .NewResult(new FeatureDecision(rule, bucketedVariation.ResultObject, FeatureDecision.DECISION_SOURCE_ROLLOUT), reasons));
                    }
                    else if (!everyoneElse)
                    {
                        //skip this logging for everyoneElse rule since this has a message not for everyoneElse
                        Logger.Log(LogLevel.INFO, reasons.AddInfo($"User \"{userId}\" is not in the traffic group for targeting rule \"{loggingKey}\". Checking EveryoneElse rule now."));
                        skipToEveryoneElse = true;
                    }
                }
                else
                {
                    Logger.Log(LogLevel.DEBUG, reasons.AddInfo($"User \"{userId}\" does not meet the conditions for targeting rule \"{loggingKey}\"."));
                }

                // the last rule is special for "Everyone Else"
                index = skipToEveryoneElse ? (rolloutRulesLength - 1) : (index + 1);
            }

            return(Result <FeatureDecision> .NullResult(reasons));
        }