Skip to content

Commit 7e5cc39

Browse files
[AI-FSSDK] [FSSDK-12337] Add Feature Rollout support
Add Feature Rollout support to the Ruby SDK. During project config parsing, inject the "everyone else" variation from the flag's rollout into any experiment with type "feature_rollout", enabling correct evaluation without changes to decision logic. - Added config parsing logic to inject the everyone else rollout variation into feature_rollout experiments - Added traffic allocation entry (endOfRange=10000) for injected variation - Added get_everyone_else_variation helper to extract the last rollout rule's first variation - Added 6 unit tests covering injection, variation maps, edge cases, and backward compatibility
1 parent 0c74513 commit 7e5cc39

File tree

2 files changed

+401
-0
lines changed

2 files changed

+401
-0
lines changed

lib/optimizely/config/datafile_project_config.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,28 @@ def initialize(datafile, logger, error_handler)
205205
feature_flag['experimentIds'].each do |experiment_id|
206206
@experiment_feature_map[experiment_id] = [feature_flag['id']]
207207
end
208+
209+
# Feature Rollout support: inject the "everyone else" variation
210+
# into any experiment with type == "feature_rollout"
211+
everyone_else_variation = get_everyone_else_variation(feature_flag)
212+
next if everyone_else_variation.nil?
213+
214+
feature_flag['experimentIds'].each do |exp_id|
215+
experiment = @experiment_id_map[exp_id]
216+
next unless experiment && experiment['type'] == 'feature_rollout'
217+
218+
experiment['variations'].push(everyone_else_variation)
219+
experiment['trafficAllocation'].push(
220+
'entityId' => everyone_else_variation['id'],
221+
'endOfRange' => 10_000
222+
)
223+
@variation_key_map[experiment['key']][everyone_else_variation['key']] = everyone_else_variation
224+
@variation_id_map[experiment['key']][everyone_else_variation['id']] = everyone_else_variation
225+
@variation_id_map_by_experiment_id[exp_id][everyone_else_variation['id']] = everyone_else_variation
226+
@variation_key_map_by_experiment_id[exp_id][everyone_else_variation['key']] = everyone_else_variation
227+
variation_variables = everyone_else_variation['variables']
228+
@variation_id_to_variable_usage_map[everyone_else_variation['id']] = generate_key_map(variation_variables, 'id') if variation_variables
229+
end
208230
end
209231

210232
# Adding Holdout variations in variation id and key maps
@@ -690,6 +712,38 @@ def get_holdout(holdout_id)
690712

691713
private
692714

715+
def get_everyone_else_variation(feature_flag)
716+
# Get the "everyone else" variation for a feature flag.
717+
#
718+
# The "everyone else" rule is the last experiment in the flag's rollout,
719+
# and its first variation is the "everyone else" variation.
720+
#
721+
# feature_flag - Feature flag hash
722+
#
723+
# Returns the "everyone else" variation hash, or nil if not available.
724+
725+
rollout_id = feature_flag['rolloutId']
726+
return nil if rollout_id.nil? || rollout_id.empty?
727+
728+
rollout = @rollout_id_map[rollout_id]
729+
return nil if rollout.nil?
730+
731+
experiments = rollout['experiments']
732+
return nil if experiments.nil? || experiments.empty?
733+
734+
everyone_else_rule = experiments.last
735+
variations = everyone_else_rule['variations']
736+
return nil if variations.nil? || variations.empty?
737+
738+
variation = variations.first
739+
{
740+
'id' => variation['id'],
741+
'key' => variation['key'],
742+
'featureEnabled' => variation['featureEnabled'] == true,
743+
'variables' => variation.fetch('variables', [])
744+
}
745+
end
746+
693747
def generate_feature_variation_map(feature_flags)
694748
flag_variation_map = {}
695749
feature_flags.each do |flag|

0 commit comments

Comments
 (0)