/// <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 FeatureDecision GetVariationForFeatureExperiment(FeatureFlag featureFlag, string userId, UserAttributes filteredAttributes, ProjectConfig config)
        {
            if (featureFlag == null)
            {
                Logger.Log(LogLevel.ERROR, "Invalid feature flag provided.");
                return(null);
            }

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

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

                if (string.IsNullOrEmpty(experiment.Key))
                {
                    continue;
                }

                var variation = GetVariation(experiment, userId, config, filteredAttributes);

                if (variation != null && !string.IsNullOrEmpty(variation.Id))
                {
                    Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is bucketed into experiment \"{experiment.Key}\" of feature \"{featureFlag.Key}\".");
                    return(new FeatureDecision(experiment, variation, FeatureDecision.DECISION_SOURCE_FEATURE_TEST));
                }
            }

            Logger.Log(LogLevel.INFO, $"The user \"{userId}\" is not bucketed into any of the experiments on the feature \"{featureFlag.Key}\".");
            return(null);
        }
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 forcedDecisionResponse = user.FindValidatedForcedDecision(
                    new OptimizelyDecisionContext(featureFlag.Key, experiment?.Key),
                    config);
                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));
        }
Esempio n. 3
0
        /// <summary>
        /// Determines whether all the experiments in the feature flag
        /// belongs to the same mutex group
        /// </summary>
        /// <param name="projectConfig">The project config object</param>
        /// <param name="featureFlag">Feature flag to validate</param>
        /// <returns>true if feature flag is valid.</returns>
        public static bool IsFeatureFlagValid(ProjectConfig projectConfig, FeatureFlag featureFlag)
        {
            var experimentIds = featureFlag.ExperimentIds;

            if (experimentIds == null || experimentIds.Count <= 1)
            {
                return(true);
            }

            var groupId = projectConfig.GetExperimentFromId(experimentIds[0]).GroupId;

            for (int i = 1; i < experimentIds.Count; i++)
            {
                // Every experiment should have the same group Id.
                if (projectConfig.GetExperimentFromId(experimentIds[i]).GroupId != groupId)
                {
                    return(false);
                }
            }

            return(true);
        }
Esempio n. 4
0
        public void TestGetVariationForFeatureWhenTheUserIsBucketedIntoFeatureExperiment()
        {
            var featureFlag          = ProjectConfig.GetFeatureFlagFromKey("string_single_variable_feature");
            var expectedExperimentId = featureFlag.ExperimentIds[0];
            var expectedExperiment   = ProjectConfig.GetExperimentFromId(expectedExperimentId);
            var variation            = expectedExperiment.Variations[0];
            var expectedDecision     = new FeatureDecision(expectedExperiment, variation, FeatureDecision.DECISION_SOURCE_EXPERIMENT);

            DecisionServiceMock.Setup(ds => ds.GetVariationForFeatureExperiment(It.IsAny <FeatureFlag>(), It.IsAny <string>(),
                                                                                It.IsAny <UserAttributes>())).Returns(expectedDecision);

            var actualDecision = DecisionServiceMock.Object.GetVariationForFeature(featureFlag, "user1", new UserAttributes());

            Assert.IsTrue(TestData.CompareObjects(expectedDecision, actualDecision));
        }
        /// <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,
                                                                                 string userId,
                                                                                 UserAttributes filteredAttributes,
                                                                                 ProjectConfig config,
                                                                                 OptimizelyDecideOption[] options)
        {
            var reasons = new DecisionReasons();

            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);

                if (string.IsNullOrEmpty(experiment.Key))
                {
                    continue;
                }

                var variationResult = GetVariation(experiment, userId, config, filteredAttributes, options);
                reasons += variationResult.DecisionReasons;

                if (!string.IsNullOrEmpty(variationResult.ResultObject?.Id))
                {
                    Logger.Log(LogLevel.INFO, reasons.AddInfo($"The user \"{userId}\" is bucketed into experiment \"{experiment.Key}\" of feature \"{featureFlag.Key}\"."));
                    return(Result <FeatureDecision> .NewResult(new FeatureDecision(experiment, variationResult.ResultObject, FeatureDecision.DECISION_SOURCE_FEATURE_TEST), 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> .NewResult(null, reasons));
        }
        public void TestInit()
        {
            // Check Version
            Assert.AreEqual("4", Config.Version);

            // Check Account ID
            Assert.AreEqual("1592310167", Config.AccountId);
            // Check Project ID
            Assert.AreEqual("7720880029", Config.ProjectId);
            // Check Revision
            Assert.AreEqual("15", Config.Revision);

            // Check Group ID Map
            var expectedGroupId = CreateDictionary("7722400015", Config.GetGroup("7722400015"));

            var actual = Config.GroupIdMap;

            Assert.IsTrue(TestData.CompareObjects(expectedGroupId, actual));

            // Check Experiment Key Map
            var experimentKeyMap = new Dictionary <string, object>()
            {
                { "test_experiment", Config.GetExperimentFromKey("test_experiment") },
                { "paused_experiment", Config.GetExperimentFromKey("paused_experiment") },
                { "test_experiment_multivariate", Config.GetExperimentFromKey("test_experiment_multivariate") },
                { "test_experiment_with_feature_rollout", Config.GetExperimentFromKey("test_experiment_with_feature_rollout") },
                { "test_experiment_double_feature", Config.GetExperimentFromKey("test_experiment_double_feature") },
                { "test_experiment_integer_feature", Config.GetExperimentFromKey("test_experiment_integer_feature") },
                { "group_experiment_1", Config.GetExperimentFromKey("group_experiment_1") },
                { "group_experiment_2", Config.GetExperimentFromKey("group_experiment_2") },
                { "etag1", Config.GetExperimentFromKey("etag1") },
                { "etag2", Config.GetExperimentFromKey("etag2") },
                { "etag3", Config.GetExperimentFromKey("etag3") },
                { "etag4", Config.GetExperimentFromKey("etag4") }
            };

            Assert.IsTrue(TestData.CompareObjects(experimentKeyMap, Config.ExperimentKeyMap));

            // Check Experiment ID Map

            var experimentIdMap = new Dictionary <string, object>()
            {
                { "7716830082", Config.GetExperimentFromId("7716830082") },
                { "7716830585", Config.GetExperimentFromId("7716830585") },
                { "122230", Config.GetExperimentFromId("122230") },
                { "122235", Config.GetExperimentFromId("122235") },
                { "122238", Config.GetExperimentFromId("122238") },
                { "122241", Config.GetExperimentFromId("122241") },
                { "7723330021", Config.GetExperimentFromId("7723330021") },
                { "7718750065", Config.GetExperimentFromId("7718750065") },
                { "223", Config.GetExperimentFromId("223") },
                { "118", Config.GetExperimentFromId("118") },
                { "224", Config.GetExperimentFromId("224") },
                { "119", Config.GetExperimentFromId("119") }
            };

            Assert.IsTrue(TestData.CompareObjects(experimentIdMap, Config.ExperimentIdMap));

            // Check Event key Map
            var eventKeyMap = new Dictionary <string, object> {
                { "purchase", Config.GetEvent("purchase") }
            };

            Assert.IsTrue(TestData.CompareObjects(eventKeyMap, Config.EventKeyMap));

            // Check Attribute Key Map
            var attributeKeyMap = new Dictionary <string, object>
            {
                { "device_type", Config.GetAttribute("device_type") },
                { "location", Config.GetAttribute("location") },
                { "browser_type", Config.GetAttribute("browser_type") },
                { "boolean_key", Config.GetAttribute("boolean_key") },
                { "integer_key", Config.GetAttribute("integer_key") },
                { "double_key", Config.GetAttribute("double_key") }
            };

            Assert.IsTrue(TestData.CompareObjects(attributeKeyMap, Config.AttributeKeyMap));

            // Check Audience ID Map
            var audienceIdMap = new Dictionary <string, object>
            {
                { "7718080042", Config.GetAudience("7718080042") },
                { "11154", Config.GetAudience("11154") },
                { "100", Config.GetAudience("100") }
            };

            Assert.IsTrue(TestData.CompareObjects(audienceIdMap, Config.AudienceIdMap));

            // Check Variation Key Map
            var expectedVariationKeyMap = new Dictionary <string, object>
            {
                { "test_experiment", new Dictionary <string, object>
                  {
                      { "control", Config.GetVariationFromKey("test_experiment", "control") },
                      { "variation", Config.GetVariationFromKey("test_experiment", "variation") }
                  } },
                { "paused_experiment", new Dictionary <string, object>
                  {
                      { "control", Config.GetVariationFromKey("paused_experiment", "control") },
                      { "variation", Config.GetVariationFromKey("paused_experiment", "variation") }
                  } },
                { "group_experiment_1", new Dictionary <string, object>
                  {
                      { "group_exp_1_var_1", Config.GetVariationFromKey("group_experiment_1", "group_exp_1_var_1") },
                      { "group_exp_1_var_2", Config.GetVariationFromKey("group_experiment_1", "group_exp_1_var_2") }
                  } },
                { "group_experiment_2", new Dictionary <string, object>
                  {
                      { "group_exp_2_var_1", Config.GetVariationFromKey("group_experiment_2", "group_exp_2_var_1") },
                      { "group_exp_2_var_2", Config.GetVariationFromKey("group_experiment_2", "group_exp_2_var_2") }
                  } },
                { "test_experiment_multivariate", new Dictionary <string, object>
                  {
                      { "Fred", Config.GetVariationFromKey("test_experiment_multivariate", "Fred") },
                      { "Feorge", Config.GetVariationFromKey("test_experiment_multivariate", "Feorge") },
                      { "Gred", Config.GetVariationFromKey("test_experiment_multivariate", "Gred") },
                      { "George", Config.GetVariationFromKey("test_experiment_multivariate", "George") }
                  } },
                { "test_experiment_with_feature_rollout", new Dictionary <string, object>
                  {
                      { "control", Config.GetVariationFromKey("test_experiment_with_feature_rollout", "control") },
                      { "variation", Config.GetVariationFromKey("test_experiment_with_feature_rollout", "variation") }
                  } },
                { "test_experiment_double_feature", new Dictionary <string, object>
                  {
                      { "control", Config.GetVariationFromKey("test_experiment_double_feature", "control") },
                      { "variation", Config.GetVariationFromKey("test_experiment_double_feature", "variation") }
                  } },
                { "test_experiment_integer_feature", new Dictionary <string, object>
                  {
                      { "control", Config.GetVariationFromKey("test_experiment_integer_feature", "control") },
                      { "variation", Config.GetVariationFromKey("test_experiment_integer_feature", "variation") }
                  } },
                { "177770", new Dictionary <string, object>
                  {
                      { "177771", Config.GetVariationFromKey("177770", "177771") }
                  } },
                { "177772", new Dictionary <string, object>
                  {
                      { "177773", Config.GetVariationFromKey("177772", "177773") }
                  } },
                { "177776", new Dictionary <string, object>
                  {
                      { "177778", Config.GetVariationFromKey("177776", "177778") }
                  } },
                { "177774", new Dictionary <string, object>
                  {
                      { "177775", Config.GetVariationFromKey("177774", "177775") }
                  } },
                { "177779", new Dictionary <string, object>
                  {
                      { "177780", Config.GetVariationFromKey("177779", "177780") }
                  } },
                { "177781", new Dictionary <string, object>
                  {
                      { "177782", Config.GetVariationFromKey("177781", "177782") }
                  } },
                { "177783", new Dictionary <string, object>
                  {
                      { "177784", Config.GetVariationFromKey("177783", "177784") }
                  } },
                { "188880", new Dictionary <string, object>
                  {
                      { "188881", Config.GetVariationFromKey("188880", "188881") }
                  } },
                { "etag1", new Dictionary <string, object>
                  {
                      { "vtag1", Config.GetVariationFromKey("etag1", "vtag1") },
                      { "vtag2", Config.GetVariationFromKey("etag1", "vtag2") }
                  } },
                { "etag2", new Dictionary <string, object>
                  {
                      { "vtag3", Config.GetVariationFromKey("etag2", "vtag3") },
                      { "vtag4", Config.GetVariationFromKey("etag2", "vtag4") }
                  } },
                { "etag3", new Dictionary <string, object>
                  {
                      { "vtag5", Config.GetVariationFromKey("etag3", "vtag5") },
                      { "vtag6", Config.GetVariationFromKey("etag3", "vtag6") }
                  } },
                { "etag4", new Dictionary <string, object>
                  {
                      { "vtag7", Config.GetVariationFromKey("etag4", "vtag7") },
                      { "vtag8", Config.GetVariationFromKey("etag4", "vtag8") }
                  } }
            };

            Assert.IsTrue(TestData.CompareObjects(expectedVariationKeyMap, Config.VariationKeyMap));

            // Check Variation ID Map
            var expectedVariationIdMap = new Dictionary <string, object>
            {
                { "test_experiment", new Dictionary <string, object>
                  {
                      { "7722370027", Config.GetVariationFromId("test_experiment", "7722370027") },
                      { "7721010009", Config.GetVariationFromId("test_experiment", "7721010009") }
                  } },
                { "paused_experiment", new Dictionary <string, object>
                  {
                      { "7722370427", Config.GetVariationFromId("paused_experiment", "7722370427") },
                      { "7721010509", Config.GetVariationFromId("paused_experiment", "7721010509") }
                  } },
                { "test_experiment_multivariate", new Dictionary <string, object>
                  {
                      { "122231", Config.GetVariationFromId("test_experiment_multivariate", "122231") },
                      { "122232", Config.GetVariationFromId("test_experiment_multivariate", "122232") },
                      { "122233", Config.GetVariationFromId("test_experiment_multivariate", "122233") },
                      { "122234", Config.GetVariationFromId("test_experiment_multivariate", "122234") }
                  } },
                { "test_experiment_with_feature_rollout", new Dictionary <string, object>
                  {
                      { "122236", Config.GetVariationFromId("test_experiment_with_feature_rollout", "122236") },
                      { "122237", Config.GetVariationFromId("test_experiment_with_feature_rollout", "122237") }
                  } },
                { "test_experiment_double_feature", new Dictionary <string, object>
                  {
                      { "122239", Config.GetVariationFromId("test_experiment_double_feature", "122239") },
                      { "122240", Config.GetVariationFromId("test_experiment_double_feature", "122240") }
                  } },
                { "test_experiment_integer_feature", new Dictionary <string, object>
                  {
                      { "122242", Config.GetVariationFromId("test_experiment_integer_feature", "122242") },
                      { "122243", Config.GetVariationFromId("test_experiment_integer_feature", "122243") }
                  } },
                { "group_experiment_1", new Dictionary <string, object>
                  {
                      { "7722260071", Config.GetVariationFromId("group_experiment_1", "7722260071") },
                      { "7722360022", Config.GetVariationFromId("group_experiment_1", "7722360022") }
                  } },
                { "group_experiment_2", new Dictionary <string, object>
                  {
                      { "7713030086", Config.GetVariationFromId("group_experiment_2", "7713030086") },
                      { "7725250007", Config.GetVariationFromId("group_experiment_2", "7725250007") }
                  } },
                { "177770", new Dictionary <string, object>
                  {
                      { "177771", Config.GetVariationFromId("177770", "177771") }
                  } },
                { "177772", new Dictionary <string, object>
                  {
                      { "177773", Config.GetVariationFromId("177772", "177773") }
                  } },
                { "177776", new Dictionary <string, object>
                  {
                      { "177778", Config.GetVariationFromId("177776", "177778") }
                  } },
                { "177774", new Dictionary <string, object>
                  {
                      { "177775", Config.GetVariationFromId("177774", "177775") }
                  } },
                { "177779", new Dictionary <string, object>
                  {
                      { "177780", Config.GetVariationFromId("177779", "177780") }
                  } },
                { "177781", new Dictionary <string, object>
                  {
                      { "177782", Config.GetVariationFromId("177781", "177782") }
                  } },
                { "177783", new Dictionary <string, object>
                  {
                      { "177784", Config.GetVariationFromId("177783", "177784") }
                  } },
                { "188880", new Dictionary <string, object>
                  {
                      { "188881", Config.GetVariationFromId("188880", "188881") }
                  } },
                { "etag1", new Dictionary <string, object>
                  {
                      { "276", Config.GetVariationFromId("etag1", "276") },
                      { "277", Config.GetVariationFromId("etag1", "277") }
                  } },
                { "etag2", new Dictionary <string, object>
                  {
                      { "278", Config.GetVariationFromId("etag2", "278") },
                      { "279", Config.GetVariationFromId("etag2", "279") }
                  } },
                { "etag3", new Dictionary <string, object>
                  {
                      { "280", Config.GetVariationFromId("etag3", "280") },
                      { "281", Config.GetVariationFromId("etag3", "281") }
                  } },
                { "etag4", new Dictionary <string, object>
                  {
                      { "282", Config.GetVariationFromId("etag4", "282") },
                      { "283", Config.GetVariationFromId("etag4", "283") }
                  } }
            };

            Assert.IsTrue(TestData.CompareObjects(expectedVariationIdMap, Config.VariationIdMap));

            // Check Variation returns correct variable usage
            var featureVariableUsageInstance = new List <FeatureVariableUsage>
            {
                new FeatureVariableUsage {
                    Id = "155560", Value = "F"
                },
                new FeatureVariableUsage {
                    Id = "155561", Value = "red"
                },
            };

            var expectedVariationUsage = new Variation {
                Id = "122231", Key = "Fred", FeatureVariableUsageInstances = featureVariableUsageInstance, FeatureEnabled = true
            };
            var actualVariationUsage = Config.GetVariationFromKey("test_experiment_multivariate", "Fred");

            Assert.IsTrue(TestData.CompareObjects(expectedVariationUsage, actualVariationUsage));

            // Check Feature Key map.
            var expectedFeatureKeyMap = new Dictionary <string, FeatureFlag>
            {
                { "boolean_feature", Config.GetFeatureFlagFromKey("boolean_feature") },
                { "double_single_variable_feature", Config.GetFeatureFlagFromKey("double_single_variable_feature") },
                { "integer_single_variable_feature", Config.GetFeatureFlagFromKey("integer_single_variable_feature") },
                { "boolean_single_variable_feature", Config.GetFeatureFlagFromKey("boolean_single_variable_feature") },
                { "string_single_variable_feature", Config.GetFeatureFlagFromKey("string_single_variable_feature") },
                { "multi_variate_feature", Config.GetFeatureFlagFromKey("multi_variate_feature") },
                { "mutex_group_feature", Config.GetFeatureFlagFromKey("mutex_group_feature") },
                { "empty_feature", Config.GetFeatureFlagFromKey("empty_feature") },
                { "no_rollout_experiment_feature", Config.GetFeatureFlagFromKey("no_rollout_experiment_feature") }
            };

            Assert.IsTrue(TestData.CompareObjects(expectedFeatureKeyMap, Config.FeatureKeyMap));

            // Check Feature Key map.
            var expectedRolloutIdMap = new Dictionary <string, Rollout>
            {
                { "166660", Config.GetRolloutFromId("166660") },
                { "166661", Config.GetRolloutFromId("166661") }
            };

            Assert.IsTrue(TestData.CompareObjects(expectedRolloutIdMap, Config.RolloutIdMap));
        }