From a8a796b97201d4f27ecd5ca675b42da3ccf69a50 Mon Sep 17 00:00:00 2001 From: msohailhussain Date: Thu, 15 Jul 2021 09:56:25 -0700 Subject: [PATCH] feat: Duplicate experiment key issue with multi feature flag (#267) --- OptimizelySDK/Bucketing/Bucketer.cs | 2 +- OptimizelySDK/Config/DatafileProjectConfig.cs | 77 +++++++++++++++++-- OptimizelySDK/ProjectConfig.cs | 18 +++++ 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/OptimizelySDK/Bucketing/Bucketer.cs b/OptimizelySDK/Bucketing/Bucketer.cs index 44cf3199..0b0eaa25 100644 --- a/OptimizelySDK/Bucketing/Bucketer.cs +++ b/OptimizelySDK/Bucketing/Bucketer.cs @@ -150,7 +150,7 @@ public virtual Result Bucket(ProjectConfig config, Experiment experim } // success! - variation = config.GetVariationFromId(experiment.Key, variationId); + variation = config.GetVariationFromIdByExperimentId(experiment.Id, variationId); message = $"User [{userId}] is in variation [{variation.Key}] of experiment [{experiment.Key}]."; Logger.Log(LogLevel.INFO, reasons.AddInfo(message)); return Result.NewResult(variation, reasons); diff --git a/OptimizelySDK/Config/DatafileProjectConfig.cs b/OptimizelySDK/Config/DatafileProjectConfig.cs index fc479b0c..1033f38e 100644 --- a/OptimizelySDK/Config/DatafileProjectConfig.cs +++ b/OptimizelySDK/Config/DatafileProjectConfig.cs @@ -125,6 +125,20 @@ private Dictionary> _VariationKeyMap = new Dictionary>(); public Dictionary> VariationKeyMap { get { return _VariationKeyMap; } } + /// + /// Associative array of experiment ID to associative array of variation key to variations + /// + private Dictionary> _VariationKeyMapByExperimentId + = new Dictionary>(); + public Dictionary> VariationKeyMapByExperimentId { get { return _VariationKeyMapByExperimentId; } } + + /// + /// Associative array of experiment ID to associative array of variation key to variations + /// + private Dictionary> _VariationIdMapByExperimentId + = new Dictionary>(); + public Dictionary> VariationKeyIdByExperimentId { get { return _VariationIdMapByExperimentId; } } + /// /// Associative array of experiment key to associative array of variation ID to variations @@ -244,9 +258,10 @@ private void Initialize() TypedAudiences = TypedAudiences ?? new Audience[0]; FeatureFlags = FeatureFlags ?? new FeatureFlag[0]; Rollouts = Rollouts ?? new Rollout[0]; + _ExperimentKeyMap = new Dictionary(); _GroupIdMap = ConfigParser.GenerateMap(entities: Groups, getKey: g => g.Id.ToString(), clone: true); - _ExperimentKeyMap = ConfigParser.GenerateMap(entities: Experiments, getKey: e => e.Key, clone: true); + _ExperimentIdMap = ConfigParser.GenerateMap(entities: Experiments, getKey: e => e.Id, clone: true); _EventKeyMap = ConfigParser.GenerateMap(entities: Events, getKey: e => e.Key, clone: true); _AttributeKeyMap = ConfigParser.GenerateMap(entities: Attributes, getKey: a => a.Key, clone: true); _AudienceIdMap = ConfigParser.GenerateMap(entities: Audiences, getKey: a => a.Id.ToString(), clone: true); @@ -260,7 +275,7 @@ private void Initialize() foreach (Group group in Groups) { - var experimentsInGroup = ConfigParser.GenerateMap(group.Experiments, getKey: e => e.Key, clone: true); + var experimentsInGroup = ConfigParser.GenerateMap(group.Experiments, getKey: e => e.Id, clone: true); foreach (Experiment experiment in experimentsInGroup.Values) { experiment.GroupId = group.Id; @@ -269,22 +284,27 @@ private void Initialize() // RJE: I believe that this is equivalent to this: // $this->_experimentKeyMap = array_merge($this->_experimentKeyMap, $experimentsInGroup); - foreach (string key in experimentsInGroup.Keys) - _ExperimentKeyMap[key] = experimentsInGroup[key]; + foreach (var experiment in experimentsInGroup.Values) + _ExperimentIdMap[experiment.Id] = experiment; } - foreach (Experiment experiment in _ExperimentKeyMap.Values) + foreach (Experiment experiment in _ExperimentIdMap.Values) { _VariationKeyMap[experiment.Key] = new Dictionary(); _VariationIdMap[experiment.Key] = new Dictionary(); - _ExperimentIdMap[experiment.Id] = experiment; + _VariationIdMapByExperimentId[experiment.Id] = new Dictionary(); + _VariationKeyMapByExperimentId[experiment.Id] = new Dictionary(); + _ExperimentKeyMap[experiment.Key] = experiment; + if (experiment.Variations != null) { foreach (Variation variation in experiment.Variations) { _VariationKeyMap[experiment.Key][variation.Key] = variation; _VariationIdMap[experiment.Key][variation.Id] = variation; + _VariationKeyMapByExperimentId[experiment.Id][variation.Key] = variation; + _VariationIdMapByExperimentId[experiment.Id][variation.Id] = variation; } } } @@ -296,6 +316,8 @@ private void Initialize() { _VariationKeyMap[rolloutRule.Key] = new Dictionary(); _VariationIdMap[rolloutRule.Key] = new Dictionary(); + _VariationIdMapByExperimentId[rolloutRule.Id] = new Dictionary(); + _VariationKeyMapByExperimentId[rolloutRule.Id] = new Dictionary(); if (rolloutRule.Variations != null) { @@ -303,6 +325,8 @@ private void Initialize() { _VariationKeyMap[rolloutRule.Key][variation.Key] = variation; _VariationIdMap[rolloutRule.Key][variation.Id] = variation; + _VariationKeyMapByExperimentId[rolloutRule.Id][variation.Key] = variation; + _VariationIdMapByExperimentId[rolloutRule.Id][variation.Id] = variation; } } } @@ -474,8 +498,28 @@ public Variation GetVariationFromKey(string experimentKey, string variationKey) return new Variation(); } + /// - /// Get the Variation from the Key/ID + /// Get the Variation from the keys + /// + /// Id for Experiment + /// key for Variation + /// Variation Entity corresponding to the provided experiment key and variation key or a dummy + /// entity if keys are invalid + public Variation GetVariationFromKeyByExperimentId(string experimentId, string variationKey) + { + if (_VariationKeyMapByExperimentId.ContainsKey(experimentId) && + _VariationKeyMapByExperimentId[experimentId].ContainsKey(variationKey)) + return _VariationKeyMapByExperimentId[experimentId][variationKey]; + + string message = $@"No variation key ""{variationKey}"" defined in datafile for experiment ""{experimentId}""."; + Logger.Log(LogLevel.ERROR, message); + ErrorHandler.HandleError(new Exceptions.InvalidVariationException("Provided variation is not in datafile.")); + return new Variation(); + } + + /// + /// Get the Variation from the Key/Id /// /// key for Experiment /// ID for Variation @@ -493,6 +537,25 @@ public Variation GetVariationFromId(string experimentKey, string variationId) return new Variation(); } + /// + /// Get the Variation from the expId/varId + /// + /// ID for Experiment + /// ID for Variation + /// Variation Entity corresponding to the provided experiment key and variation ID or a dummy + /// entity if experiment ID or variation ID is invalid + public Variation GetVariationFromIdByExperimentId(string experimentId, string variationId) + { + if (_VariationIdMapByExperimentId.ContainsKey(experimentId) && + _VariationIdMapByExperimentId[experimentId].ContainsKey(variationId)) + return _VariationIdMapByExperimentId[experimentId][variationId]; + + string message = $@"No variation ID ""{variationId}"" defined in datafile for experiment ""{experimentId}""."; + Logger.Log(LogLevel.ERROR, message); + ErrorHandler.HandleError(new Exceptions.InvalidVariationException("Provided variation is not in datafile.")); + return new Variation(); + } + /// /// Get the feature from the key /// diff --git a/OptimizelySDK/ProjectConfig.cs b/OptimizelySDK/ProjectConfig.cs index 0b56c2e2..c97432f6 100644 --- a/OptimizelySDK/ProjectConfig.cs +++ b/OptimizelySDK/ProjectConfig.cs @@ -209,6 +209,15 @@ public interface ProjectConfig /// entity if keys are invalid Variation GetVariationFromKey(string experimentKey, string variationKey); + /// + /// Get the Variation from the keys + /// + /// ID for Experiment + /// key for Variation + /// Variation Entity corresponding to the provided experiment key and variation key or a dummy + /// entity if keys are invalid + Variation GetVariationFromKeyByExperimentId(string experimentId, string variationKey); + /// /// Get the Variation from the Key/ID /// @@ -218,6 +227,15 @@ public interface ProjectConfig /// entity if key or ID is invalid Variation GetVariationFromId(string experimentKey, string variationId); + /// + /// Get the Variation from the Key/ID + /// + /// ID for Experiment + /// ID for Variation + /// Variation Entity corresponding to the provided experiment key and variation ID or a dummy + /// entity if key or ID is invalid + Variation GetVariationFromIdByExperimentId(string experimentId, string variationId); + /// /// Get the feature from the key ///