Skip to content

[FSSDK-11185] Update: Send CMAB uuid in impression events #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
de61931
update: Extend LRUCache with remove method and corresponding tests
FarhanAnjum-opti Jul 1, 2025
01e3a9f
update: Clean up whitespace in LRUCache implementation and tests
FarhanAnjum-opti Jul 1, 2025
40d0571
update: Extend copyright notice to include 2025
FarhanAnjum-opti Jul 1, 2025
2282eb1
update: Implement Default CMAB Service
FarhanAnjum-opti Jul 9, 2025
8ca3aee
update: Enable keyword initialization for CmabDecision and CmabCacheV…
FarhanAnjum-opti Jul 9, 2025
761bc43
update: Refactor bucketing logic to handle empty traffic ranges and i…
FarhanAnjum-opti Jul 14, 2025
ebb1b7d
update: Add support for CMAB traffic allocation in bucketing logic
FarhanAnjum-opti Jul 14, 2025
d6dd3aa
update: Enhance DecisionService to support CMAB traffic allocation an…
FarhanAnjum-opti Jul 14, 2025
75ee816
update: Integrate CMAB decision logic into DecisionService and update…
FarhanAnjum-opti Jul 16, 2025
f48dbc2
update: Refactor DecisionService to return DecisionResult struct inst…
FarhanAnjum-opti Jul 18, 2025
0e9e4f8
update: Integrate CMAB components into Project class and enhance deci…
FarhanAnjum-opti Jul 23, 2025
35cea84
update: Refactor CMAB traffic allocation handling and enhance decisio…
FarhanAnjum-opti Jul 23, 2025
20ecb66
Merge branch 'master' into farhan-anjum/FSSDK-11176-update-decision-s…
FarhanAnjum-opti Jul 23, 2025
9c31bb8
update: Refactor OptimizelyDecision instantiation to use keyword argu…
FarhanAnjum-opti Jul 23, 2025
d35161b
update: Enhance send_impression method to include CMAB UUID and add t…
FarhanAnjum-opti Jul 23, 2025
7851796
Merge branch 'master' into farhan-anjum/FSSDK-11185-update-impression…
FarhanAnjum-opti Jul 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/optimizely.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def create_optimizely_decision(user_context, flag_key, decision, reasons, decide
end

if !decide_options.include?(OptimizelyDecideOption::DISABLE_DECISION_EVENT) && (decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || config.send_flag_decisions)
send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes)
send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes, decision&.cmab_uuid)
decision_event_dispatched = true
end

Expand Down Expand Up @@ -1244,7 +1244,7 @@ def validate_instantiation_options
raise InvalidInputError, 'event_dispatcher'
end

def send_impression(config, experiment, variation_key, flag_key, rule_key, enabled, rule_type, user_id, attributes = nil)
def send_impression(config, experiment, variation_key, flag_key, rule_key, enabled, rule_type, user_id, attributes = nil, cmab_uuid = nil)
if experiment.nil?
experiment = {
'id' => '',
Expand Down Expand Up @@ -1276,6 +1276,7 @@ def send_impression(config, experiment, variation_key, flag_key, rule_key, enabl
variation_key: variation_key,
enabled: enabled
}
metadata[:cmab_uuid] = cmab_uuid unless cmab_uuid.nil?

user_event = UserEventFactory.create_impression_event(config, experiment, variation_id, metadata, user_id, attributes)
@event_processor.process(user_event)
Expand Down
1 change: 1 addition & 0 deletions spec/optimizely_user_context_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@
decision = user_context_obj.decide(feature_key, [Optimizely::Decide::OptimizelyDecideOption::INCLUDE_REASONS])
expect(decision.variation_key).to eq('18257766532')
expect(decision.rule_key).to eq('18322080788')
# puts decision.reasons
expect(decision.reasons).to include('Invalid variation is mapped to flag (feature_1), rule (exp_with_audience) and user (tester) in the forced decision map.')

# delivery-rule-to-decision
Expand Down
97 changes: 97 additions & 0 deletions spec/project_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4308,6 +4308,103 @@ def callback(_args); end
expect(decision.reasons).to include('CMAB service failed to fetch decision')
end
end
describe 'CMAB experiments' do
it 'should include CMAB UUID in dispatched event when decision service returns CMAB result' do
# Use an existing feature flag from the test config
feature_flag_key = 'boolean_single_variable_feature'

# Get an existing experiment that actually exists in the datafile
# Looking at the test config, let's use experiment ID '122230' which exists
existing_experiment = project_config.get_experiment_from_id('122230')

# Modify the existing experiment to be a CMAB experiment
cmab_experiment = existing_experiment.dup
cmab_experiment['trafficAllocation'] = [] # Empty for CMAB
cmab_experiment['cmab'] = {'attributeIds' => %w[808797688 808797689], 'trafficAllocation' => 4000}

# Mock the config to return our modified CMAB experiment
allow(project_instance.config_manager.config).to receive(:get_experiment_from_id)
.with('122230')
.and_return(cmab_experiment)

allow(project_instance.config_manager.config).to receive(:experiment_running?)
.with(cmab_experiment)
.and_return(true)

# Get the feature flag and update it to reference our CMAB experiment
feature_flag = project_instance.config_manager.config.get_feature_flag_from_key(feature_flag_key)
feature_flag['experimentIds'] = ['122230']

# Use existing variations from the original experiment
variation_to_use = existing_experiment['variations'][0]

# Create a decision with CMAB UUID
expected_cmab_uuid = 'uuid-cmab'
decision_with_cmab = Optimizely::DecisionService::Decision.new(
cmab_experiment,
variation_to_use,
Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'],
expected_cmab_uuid
)

decision_result_with_cmab = Optimizely::DecisionService::DecisionResult.new(
decision_with_cmab,
false,
[]
)

# Mock get_variations_for_feature_list to return CMAB result
allow(project_instance.decision_service).to receive(:get_variations_for_feature_list)
.and_return([decision_result_with_cmab])

# Set up time and UUID mocks for consistent event data
allow(Time).to receive(:now).and_return(time_now)
allow(SecureRandom).to receive(:uuid).and_return('a68cf1ad-0393-4e18-af87-efe8f01a7c9c')

# Create array to capture dispatched events
dispatched_events = []
allow(project_instance.event_dispatcher).to receive(:dispatch_event) do |event|
dispatched_events << event
end

user_context = project_instance.create_user_context('test_user')
decision = user_context.decide(feature_flag_key)

# Wait for batch processing thread to send event
sleep 0.1 until project_instance.event_processor.event_queue.empty?

# Verify the decision contains expected information
expect(decision.enabled).to eq(true)
expect(decision.variation_key).to eq(variation_to_use['key'])
expect(decision.rule_key).to eq(existing_experiment['key'])
expect(decision.flag_key).to eq(feature_flag_key)

# Verify an event was dispatched
expect(dispatched_events.length).to eq(1)

dispatched_event = dispatched_events[0]

# Remove the puts statement and verify the event structure and CMAB UUID
expect(dispatched_event.params).to have_key(:visitors)
expect(dispatched_event.params[:visitors].length).to be > 0
expect(dispatched_event.params[:visitors][0]).to have_key(:snapshots)
expect(dispatched_event.params[:visitors][0][:snapshots].length).to be > 0
expect(dispatched_event.params[:visitors][0][:snapshots][0]).to have_key(:decisions)
expect(dispatched_event.params[:visitors][0][:snapshots][0][:decisions].length).to be > 0

# Get the metadata and assert CMAB UUID
metadata = dispatched_event.params[:visitors][0][:snapshots][0][:decisions][0][:metadata]
expect(metadata).to have_key(:cmab_uuid)
expect(metadata[:cmab_uuid]).to eq(expected_cmab_uuid)

# Also verify other expected metadata fields
expect(metadata[:flag_key]).to eq(feature_flag_key)
expect(metadata[:rule_key]).to eq('test_experiment_multivariate')
expect(metadata[:rule_type]).to eq('feature-test')
expect(metadata[:variation_key]).to eq('Fred')
expect(metadata[:enabled]).to eq(true)
end
end
end

describe '#decide_all' do
Expand Down