optimizely-sdk 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/optimizely/audience.rb +15 -1
- data/lib/optimizely/bucketer.rb +34 -13
- data/lib/optimizely/cmab/cmab_client.rb +230 -0
- data/lib/optimizely/cmab/cmab_service.rb +218 -0
- data/lib/optimizely/config/datafile_project_config.rb +140 -2
- data/lib/optimizely/config_manager/http_project_config_manager.rb +1 -1
- data/lib/optimizely/decide/optimizely_decide_option.rb +3 -0
- data/lib/optimizely/decide/optimizely_decision.rb +19 -0
- data/lib/optimizely/decision_service.rb +252 -57
- data/lib/optimizely/event/entity/event_context.rb +5 -2
- data/lib/optimizely/event/event_factory.rb +8 -2
- data/lib/optimizely/event/user_event_factory.rb +2 -0
- data/lib/optimizely/event_builder.rb +15 -5
- data/lib/optimizely/exceptions.rb +24 -0
- data/lib/optimizely/helpers/constants.rb +46 -0
- data/lib/optimizely/helpers/sdk_settings.rb +5 -2
- data/lib/optimizely/helpers/validator.rb +2 -2
- data/lib/optimizely/odp/lru_cache.rb +14 -1
- data/lib/optimizely/optimizely_factory.rb +56 -1
- data/lib/optimizely/project_config.rb +6 -0
- data/lib/optimizely/version.rb +1 -1
- data/lib/optimizely.rb +59 -20
- metadata +4 -2
|
@@ -201,6 +201,12 @@ module Optimizely
|
|
|
201
201
|
},
|
|
202
202
|
'forcedVariations' => {
|
|
203
203
|
'type' => 'object'
|
|
204
|
+
},
|
|
205
|
+
'cmab' => {
|
|
206
|
+
'type' => 'object'
|
|
207
|
+
},
|
|
208
|
+
'holdouts' => {
|
|
209
|
+
'type' => 'array'
|
|
204
210
|
}
|
|
205
211
|
},
|
|
206
212
|
'required' => %w[
|
|
@@ -303,6 +309,43 @@ module Optimizely
|
|
|
303
309
|
},
|
|
304
310
|
'required' => %w[key]
|
|
305
311
|
}
|
|
312
|
+
},
|
|
313
|
+
'cmab' => {
|
|
314
|
+
'type' => 'object',
|
|
315
|
+
'properties' => {
|
|
316
|
+
'attributeIds' => {
|
|
317
|
+
'type' => 'array',
|
|
318
|
+
'items' => {'type' => 'string'}
|
|
319
|
+
},
|
|
320
|
+
'trafficAllocation' => {
|
|
321
|
+
'type' => 'integer'
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
'holdouts' => {
|
|
326
|
+
'type' => 'array',
|
|
327
|
+
'items' => {
|
|
328
|
+
'type' => 'object',
|
|
329
|
+
'properties' => {
|
|
330
|
+
'id' => {
|
|
331
|
+
'type' => 'string'
|
|
332
|
+
},
|
|
333
|
+
'key' => {
|
|
334
|
+
'type' => 'string'
|
|
335
|
+
},
|
|
336
|
+
'status' => {
|
|
337
|
+
'type' => 'string'
|
|
338
|
+
},
|
|
339
|
+
'includedFlags' => {
|
|
340
|
+
'type' => 'array',
|
|
341
|
+
'items' => {'type' => 'string'}
|
|
342
|
+
},
|
|
343
|
+
'excludedFlags' => {
|
|
344
|
+
'type' => 'array',
|
|
345
|
+
'items' => {'type' => 'string'}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
306
349
|
}
|
|
307
350
|
},
|
|
308
351
|
'required' => %w[
|
|
@@ -454,6 +497,9 @@ module Optimizely
|
|
|
454
497
|
'IF_MODIFIED_SINCE' => 'If-Modified-Since',
|
|
455
498
|
'LAST_MODIFIED' => 'Last-Modified'
|
|
456
499
|
}.freeze
|
|
500
|
+
|
|
501
|
+
CMAB_FETCH_FAILED = 'CMAB decision fetch failed (%s).'
|
|
502
|
+
INVALID_CMAB_FETCH_RESPONSE = 'Invalid CMAB fetch response'
|
|
457
503
|
end
|
|
458
504
|
end
|
|
459
505
|
end
|
|
@@ -22,7 +22,7 @@ module Optimizely
|
|
|
22
22
|
module Helpers
|
|
23
23
|
class OptimizelySdkSettings
|
|
24
24
|
attr_accessor :odp_disabled, :segments_cache_size, :segments_cache_timeout_in_secs, :odp_segments_cache, :odp_segment_manager,
|
|
25
|
-
:odp_event_manager, :fetch_segments_timeout, :odp_event_timeout, :odp_flush_interval
|
|
25
|
+
:odp_event_manager, :fetch_segments_timeout, :odp_event_timeout, :odp_flush_interval, :cmab_prediction_endpoint
|
|
26
26
|
|
|
27
27
|
# Contains configuration used for Optimizely Project initialization.
|
|
28
28
|
#
|
|
@@ -35,6 +35,7 @@ module Optimizely
|
|
|
35
35
|
# @param odp_segment_request_timeout - Time to wait in seconds for fetch_qualified_segments (optional. default = 10).
|
|
36
36
|
# @param odp_event_request_timeout - Time to wait in seconds for send_odp_events (optional. default = 10).
|
|
37
37
|
# @param odp_event_flush_interval - Time to wait in seconds for odp events to accumulate before sending (optional. default = 1).
|
|
38
|
+
# @param cmab_prediction_endpoint - Custom CMAB prediction endpoint URL template (optional). Use %s as placeholder for rule_id. Defaults to production endpoint if not provided.
|
|
38
39
|
def initialize(
|
|
39
40
|
disable_odp: false,
|
|
40
41
|
segments_cache_size: Constants::ODP_SEGMENTS_CACHE_CONFIG[:DEFAULT_CAPACITY],
|
|
@@ -44,7 +45,8 @@ module Optimizely
|
|
|
44
45
|
odp_event_manager: nil,
|
|
45
46
|
odp_segment_request_timeout: nil,
|
|
46
47
|
odp_event_request_timeout: nil,
|
|
47
|
-
odp_event_flush_interval: nil
|
|
48
|
+
odp_event_flush_interval: nil,
|
|
49
|
+
cmab_prediction_endpoint: nil
|
|
48
50
|
)
|
|
49
51
|
@odp_disabled = disable_odp
|
|
50
52
|
@segments_cache_size = segments_cache_size
|
|
@@ -55,6 +57,7 @@ module Optimizely
|
|
|
55
57
|
@fetch_segments_timeout = odp_segment_request_timeout
|
|
56
58
|
@odp_event_timeout = odp_event_request_timeout
|
|
57
59
|
@odp_flush_interval = odp_event_flush_interval
|
|
60
|
+
@cmab_prediction_endpoint = cmab_prediction_endpoint
|
|
58
61
|
end
|
|
59
62
|
end
|
|
60
63
|
end
|
|
@@ -122,11 +122,11 @@ module Optimizely
|
|
|
122
122
|
|
|
123
123
|
return false unless variables.respond_to?(:each) && !variables.empty?
|
|
124
124
|
|
|
125
|
-
is_valid = true
|
|
125
|
+
is_valid = true
|
|
126
126
|
if variables.include? :user_id
|
|
127
127
|
# Empty str is a valid user ID.
|
|
128
128
|
unless variables[:user_id].is_a?(String)
|
|
129
|
-
is_valid = false
|
|
129
|
+
is_valid = false
|
|
130
130
|
logger.log(level, "#{Constants::INPUT_VARIABLES['USER_ID']} is invalid")
|
|
131
131
|
end
|
|
132
132
|
variables.delete :user_id
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#
|
|
4
|
-
# Copyright 2022, Optimizely and contributors
|
|
4
|
+
# Copyright 2022-2025, Optimizely and contributors
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
# you may not use this file except in compliance with the License.
|
|
@@ -91,6 +91,19 @@ module Optimizely
|
|
|
91
91
|
|
|
92
92
|
@cache_mutex.synchronize { @map[key]&.value }
|
|
93
93
|
end
|
|
94
|
+
|
|
95
|
+
# Remove the element associated with the provided key from the cache
|
|
96
|
+
#
|
|
97
|
+
# @param key - The key to remove
|
|
98
|
+
|
|
99
|
+
def remove(key)
|
|
100
|
+
return if @capacity <= 0
|
|
101
|
+
|
|
102
|
+
@cache_mutex.synchronize do
|
|
103
|
+
@map.delete(key)
|
|
104
|
+
end
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
94
107
|
end
|
|
95
108
|
|
|
96
109
|
class CacheElement
|
|
@@ -22,6 +22,8 @@ require 'optimizely/event_dispatcher'
|
|
|
22
22
|
require 'optimizely/event/batch_event_processor'
|
|
23
23
|
require 'optimizely/logger'
|
|
24
24
|
require 'optimizely/notification_center'
|
|
25
|
+
require 'optimizely/cmab/cmab_client'
|
|
26
|
+
require 'optimizely/cmab/cmab_service'
|
|
25
27
|
|
|
26
28
|
module Optimizely
|
|
27
29
|
class OptimizelyFactory
|
|
@@ -83,6 +85,46 @@ module Optimizely
|
|
|
83
85
|
@blocking_timeout = blocking_timeout
|
|
84
86
|
end
|
|
85
87
|
|
|
88
|
+
# Convenience method for setting CMAB cache size.
|
|
89
|
+
# @param cache_size Integer - Maximum number of items in CMAB cache.
|
|
90
|
+
# @param logger - Optional LoggerInterface Provides a log method to log messages.
|
|
91
|
+
def self.cmab_cache_size(cache_size, logger = NoOpLogger.new)
|
|
92
|
+
unless cache_size.is_a?(Integer) && cache_size.positive?
|
|
93
|
+
logger.log(
|
|
94
|
+
Logger::ERROR,
|
|
95
|
+
"CMAB cache size is invalid, setting to default size #{Optimizely::DefaultCmabCacheOptions::DEFAULT_CMAB_CACHE_SIZE}."
|
|
96
|
+
)
|
|
97
|
+
return
|
|
98
|
+
end
|
|
99
|
+
@cmab_cache_size = cache_size
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Convenience method for setting CMAB cache TTL.
|
|
103
|
+
# @param cache_ttl Numeric - Time in seconds for cache entries to live.
|
|
104
|
+
# @param logger - Optional LoggerInterface Provides a log method to log messages.
|
|
105
|
+
def self.cmab_cache_ttl(cache_ttl, logger = NoOpLogger.new)
|
|
106
|
+
unless cache_ttl.is_a?(Numeric) && cache_ttl.positive?
|
|
107
|
+
logger.log(
|
|
108
|
+
Logger::ERROR,
|
|
109
|
+
"CMAB cache TTL is invalid, setting to default TTL #{Optimizely::DefaultCmabCacheOptions::DEFAULT_CMAB_CACHE_TIMEOUT}."
|
|
110
|
+
)
|
|
111
|
+
return
|
|
112
|
+
end
|
|
113
|
+
@cmab_cache_ttl = cache_ttl
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Convenience method for setting custom CMAB cache.
|
|
117
|
+
# @param custom_cache - Cache implementation responding to lookup, save, remove, and reset methods.
|
|
118
|
+
def self.cmab_custom_cache(custom_cache)
|
|
119
|
+
@cmab_custom_cache = custom_cache
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Convenience method for setting custom CMAB prediction endpoint.
|
|
123
|
+
# @param prediction_endpoint String - Custom URL template for CMAB prediction API. Use %s as placeholder for rule_id.
|
|
124
|
+
def self.cmab_prediction_endpoint(prediction_endpoint)
|
|
125
|
+
@cmab_prediction_endpoint = prediction_endpoint
|
|
126
|
+
end
|
|
127
|
+
|
|
86
128
|
# Returns a new optimizely instance.
|
|
87
129
|
#
|
|
88
130
|
# @params sdk_key - Required String uniquely identifying the fallback datafile corresponding to project.
|
|
@@ -165,6 +207,18 @@ module Optimizely
|
|
|
165
207
|
notification_center: notification_center
|
|
166
208
|
)
|
|
167
209
|
|
|
210
|
+
# Initialize CMAB components
|
|
211
|
+
cmab_prediction_endpoint = nil
|
|
212
|
+
cmab_prediction_endpoint = settings.cmab_prediction_endpoint if settings&.cmab_prediction_endpoint
|
|
213
|
+
cmab_prediction_endpoint ||= @cmab_prediction_endpoint
|
|
214
|
+
|
|
215
|
+
cmab_client = DefaultCmabClient.new(logger: logger, prediction_endpoint: cmab_prediction_endpoint)
|
|
216
|
+
cmab_cache = @cmab_custom_cache || LRUCache.new(
|
|
217
|
+
@cmab_cache_size || Optimizely::DefaultCmabCacheOptions::DEFAULT_CMAB_CACHE_SIZE,
|
|
218
|
+
@cmab_cache_ttl || Optimizely::DefaultCmabCacheOptions::DEFAULT_CMAB_CACHE_TIMEOUT
|
|
219
|
+
)
|
|
220
|
+
cmab_service = DefaultCmabService.new(cmab_cache, cmab_client, logger)
|
|
221
|
+
|
|
168
222
|
Optimizely::Project.new(
|
|
169
223
|
datafile: datafile,
|
|
170
224
|
event_dispatcher: event_dispatcher,
|
|
@@ -176,7 +230,8 @@ module Optimizely
|
|
|
176
230
|
config_manager: config_manager,
|
|
177
231
|
notification_center: notification_center,
|
|
178
232
|
event_processor: event_processor,
|
|
179
|
-
settings: settings
|
|
233
|
+
settings: settings,
|
|
234
|
+
cmab_service: cmab_service
|
|
180
235
|
)
|
|
181
236
|
end
|
|
182
237
|
end
|
|
@@ -62,6 +62,8 @@ module Optimizely
|
|
|
62
62
|
|
|
63
63
|
def all_segments; end
|
|
64
64
|
|
|
65
|
+
def region; end
|
|
66
|
+
|
|
65
67
|
def experiment_running?(experiment); end
|
|
66
68
|
|
|
67
69
|
def get_experiment_from_key(experiment_key); end
|
|
@@ -86,6 +88,10 @@ module Optimizely
|
|
|
86
88
|
|
|
87
89
|
def get_attribute_id(attribute_key); end
|
|
88
90
|
|
|
91
|
+
def get_attribute_by_key(attribute_key); end
|
|
92
|
+
|
|
93
|
+
def get_attribute_key_by_id(attribute_id); end
|
|
94
|
+
|
|
89
95
|
def variation_id_exists?(experiment_id, variation_id); end
|
|
90
96
|
|
|
91
97
|
def get_feature_flag_from_key(feature_flag_key); end
|
data/lib/optimizely/version.rb
CHANGED
data/lib/optimizely.rb
CHANGED
|
@@ -43,6 +43,8 @@ require_relative 'optimizely/odp/lru_cache'
|
|
|
43
43
|
require_relative 'optimizely/odp/odp_manager'
|
|
44
44
|
require_relative 'optimizely/helpers/sdk_settings'
|
|
45
45
|
require_relative 'optimizely/user_profile_tracker'
|
|
46
|
+
require_relative 'optimizely/cmab/cmab_client'
|
|
47
|
+
require_relative 'optimizely/cmab/cmab_service'
|
|
46
48
|
|
|
47
49
|
module Optimizely
|
|
48
50
|
class Project
|
|
@@ -84,7 +86,8 @@ module Optimizely
|
|
|
84
86
|
event_processor: nil,
|
|
85
87
|
default_decide_options: [],
|
|
86
88
|
event_processor_options: {},
|
|
87
|
-
settings: nil
|
|
89
|
+
settings: nil,
|
|
90
|
+
cmab_service: nil
|
|
88
91
|
)
|
|
89
92
|
@logger = logger || NoOpLogger.new
|
|
90
93
|
@error_handler = error_handler || NoOpErrorHandler.new
|
|
@@ -131,7 +134,24 @@ module Optimizely
|
|
|
131
134
|
|
|
132
135
|
setup_odp!(@config_manager.sdk_key)
|
|
133
136
|
|
|
134
|
-
|
|
137
|
+
# Initialize CMAB components if cmab service is nil
|
|
138
|
+
if cmab_service.nil?
|
|
139
|
+
@cmab_client = DefaultCmabClient.new(
|
|
140
|
+
http_client: nil,
|
|
141
|
+
retry_config: CmabRetryConfig.new,
|
|
142
|
+
logger: @logger
|
|
143
|
+
)
|
|
144
|
+
@cmab_cache = LRUCache.new(Optimizely::DefaultCmabCacheOptions::DEFAULT_CMAB_CACHE_SIZE, Optimizely::DefaultCmabCacheOptions::DEFAULT_CMAB_CACHE_TIMEOUT)
|
|
145
|
+
@cmab_service = DefaultCmabService.new(
|
|
146
|
+
@cmab_cache,
|
|
147
|
+
@cmab_client,
|
|
148
|
+
@logger
|
|
149
|
+
)
|
|
150
|
+
else
|
|
151
|
+
@cmab_service = cmab_service
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
@decision_service = DecisionService.new(@logger, @cmab_service, @user_profile_service)
|
|
135
155
|
|
|
136
156
|
@event_processor = if event_processor.respond_to?(:process)
|
|
137
157
|
event_processor
|
|
@@ -185,18 +205,23 @@ module Optimizely
|
|
|
185
205
|
feature_flag = config.get_feature_flag_from_key(flag_key)
|
|
186
206
|
experiment = nil
|
|
187
207
|
decision_source = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
|
|
208
|
+
experiment_id = nil
|
|
209
|
+
variation_id = nil
|
|
210
|
+
|
|
188
211
|
# Send impression event if Decision came from a feature test and decide options doesn't include disableDecisionEvent
|
|
189
212
|
if decision.is_a?(Optimizely::DecisionService::Decision)
|
|
190
213
|
experiment = decision.experiment
|
|
191
214
|
rule_key = experiment ? experiment['key'] : nil
|
|
192
|
-
|
|
215
|
+
experiment_id = experiment ? experiment['id'] : nil
|
|
216
|
+
variation = decision.variation
|
|
193
217
|
variation_key = variation ? variation['key'] : nil
|
|
218
|
+
variation_id = variation ? variation['id'] : nil
|
|
194
219
|
feature_enabled = variation ? variation['featureEnabled'] : false
|
|
195
220
|
decision_source = decision.source
|
|
196
221
|
end
|
|
197
222
|
|
|
198
223
|
if !decide_options.include?(OptimizelyDecideOption::DISABLE_DECISION_EVENT) && (decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || config.send_flag_decisions)
|
|
199
|
-
send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes)
|
|
224
|
+
send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes, decision&.cmab_uuid)
|
|
200
225
|
decision_event_dispatched = true
|
|
201
226
|
end
|
|
202
227
|
|
|
@@ -214,14 +239,16 @@ module Optimizely
|
|
|
214
239
|
@notification_center.send_notifications(
|
|
215
240
|
NotificationCenter::NOTIFICATION_TYPES[:DECISION],
|
|
216
241
|
Helpers::Constants::DECISION_NOTIFICATION_TYPES['FLAG'],
|
|
217
|
-
user_id,
|
|
242
|
+
user_id, attributes || {},
|
|
218
243
|
flag_key: flag_key,
|
|
219
244
|
enabled: feature_enabled,
|
|
220
245
|
variables: all_variables,
|
|
221
246
|
variation_key: variation_key,
|
|
222
247
|
rule_key: rule_key,
|
|
223
248
|
reasons: should_include_reasons ? reasons : [],
|
|
224
|
-
decision_event_dispatched: decision_event_dispatched
|
|
249
|
+
decision_event_dispatched: decision_event_dispatched,
|
|
250
|
+
experiment_id: experiment_id,
|
|
251
|
+
variation_id: variation_id
|
|
225
252
|
)
|
|
226
253
|
|
|
227
254
|
OptimizelyDecision.new(
|
|
@@ -330,7 +357,7 @@ module Optimizely
|
|
|
330
357
|
|
|
331
358
|
# If the feature flag is nil, create a default OptimizelyDecision and move to the next key
|
|
332
359
|
if feature_flag.nil?
|
|
333
|
-
decisions[key] = OptimizelyDecision.new(nil, false, nil, nil, key, user_context, [])
|
|
360
|
+
decisions[key] = OptimizelyDecision.new(variation_key: nil, enabled: false, variables: nil, rule_key: nil, flag_key: key, user_context: user_context, reasons: [])
|
|
334
361
|
next
|
|
335
362
|
end
|
|
336
363
|
valid_keys.push(key)
|
|
@@ -351,9 +378,17 @@ module Optimizely
|
|
|
351
378
|
decision_list = @decision_service.get_variations_for_feature_list(config, flags_without_forced_decision, user_context, decide_options)
|
|
352
379
|
|
|
353
380
|
flags_without_forced_decision.each_with_index do |flag, i|
|
|
354
|
-
decision = decision_list[i]
|
|
355
|
-
reasons = decision_list[i]
|
|
381
|
+
decision = decision_list[i].decision
|
|
382
|
+
reasons = decision_list[i].reasons
|
|
383
|
+
error = decision_list[i].error
|
|
356
384
|
flag_key = flag['key']
|
|
385
|
+
# store error decision against key and remove key from valid keys
|
|
386
|
+
if error
|
|
387
|
+
optimizely_decision = OptimizelyDecision.new_error_decision(flag_key, user_context, reasons)
|
|
388
|
+
decisions[flag_key] = optimizely_decision
|
|
389
|
+
valid_keys.delete(flag_key) if valid_keys.include?(flag_key)
|
|
390
|
+
next
|
|
391
|
+
end
|
|
357
392
|
flag_decisions[flag_key] = decision
|
|
358
393
|
decision_reasons_dict[flag_key] ||= []
|
|
359
394
|
decision_reasons_dict[flag_key].push(*reasons)
|
|
@@ -592,14 +627,14 @@ module Optimizely
|
|
|
592
627
|
end
|
|
593
628
|
|
|
594
629
|
user_context = OptimizelyUserContext.new(self, user_id, attributes, identify: false)
|
|
595
|
-
|
|
596
|
-
|
|
630
|
+
decision_result = @decision_service.get_variation_for_feature(config, feature_flag, user_context)
|
|
631
|
+
decision = decision_result.decision
|
|
597
632
|
feature_enabled = false
|
|
598
633
|
source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
|
|
599
634
|
if decision.is_a?(Optimizely::DecisionService::Decision)
|
|
600
635
|
variation = decision['variation']
|
|
601
636
|
feature_enabled = variation['featureEnabled']
|
|
602
|
-
if decision.source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
|
|
637
|
+
if decision.source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || decision.source == Optimizely::DecisionService::DECISION_SOURCES['HOLDOUT']
|
|
603
638
|
source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
|
|
604
639
|
source_info = {
|
|
605
640
|
experiment_key: decision.experiment['key'],
|
|
@@ -625,7 +660,7 @@ module Optimizely
|
|
|
625
660
|
@notification_center.send_notifications(
|
|
626
661
|
NotificationCenter::NOTIFICATION_TYPES[:DECISION],
|
|
627
662
|
Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE'],
|
|
628
|
-
user_id,
|
|
663
|
+
user_id, attributes || {},
|
|
629
664
|
feature_key: feature_flag_key,
|
|
630
665
|
feature_enabled: feature_enabled,
|
|
631
666
|
source: source_string,
|
|
@@ -832,7 +867,8 @@ module Optimizely
|
|
|
832
867
|
end
|
|
833
868
|
|
|
834
869
|
user_context = OptimizelyUserContext.new(self, user_id, attributes, identify: false)
|
|
835
|
-
|
|
870
|
+
decision_result = @decision_service.get_variation_for_feature(config, feature_flag, user_context)
|
|
871
|
+
decision = decision_result.decision
|
|
836
872
|
variation = decision ? decision['variation'] : nil
|
|
837
873
|
feature_enabled = variation ? variation['featureEnabled'] : false
|
|
838
874
|
all_variables = {}
|
|
@@ -853,7 +889,7 @@ module Optimizely
|
|
|
853
889
|
|
|
854
890
|
@notification_center.send_notifications(
|
|
855
891
|
NotificationCenter::NOTIFICATION_TYPES[:DECISION],
|
|
856
|
-
Helpers::Constants::DECISION_NOTIFICATION_TYPES['ALL_FEATURE_VARIABLES'], user_id,
|
|
892
|
+
Helpers::Constants::DECISION_NOTIFICATION_TYPES['ALL_FEATURE_VARIABLES'], user_id, attributes || {},
|
|
857
893
|
feature_key: feature_flag_key,
|
|
858
894
|
feature_enabled: feature_enabled,
|
|
859
895
|
source: source_string,
|
|
@@ -1022,7 +1058,8 @@ module Optimizely
|
|
|
1022
1058
|
user_context = OptimizelyUserContext.new(self, user_id, attributes, identify: false)
|
|
1023
1059
|
user_profile_tracker = UserProfileTracker.new(user_id, @user_profile_service, @logger)
|
|
1024
1060
|
user_profile_tracker.load_user_profile
|
|
1025
|
-
|
|
1061
|
+
variation_result = @decision_service.get_variation(config, experiment_id, user_context, user_profile_tracker)
|
|
1062
|
+
variation_id = variation_result.variation_id
|
|
1026
1063
|
user_profile_tracker.save_user_profile
|
|
1027
1064
|
variation = config.get_variation_from_id(experiment_key, variation_id) unless variation_id.nil?
|
|
1028
1065
|
variation_key = variation['key'] if variation
|
|
@@ -1033,7 +1070,7 @@ module Optimizely
|
|
|
1033
1070
|
end
|
|
1034
1071
|
@notification_center.send_notifications(
|
|
1035
1072
|
NotificationCenter::NOTIFICATION_TYPES[:DECISION],
|
|
1036
|
-
decision_notification_type, user_id,
|
|
1073
|
+
decision_notification_type, user_id, attributes || {},
|
|
1037
1074
|
experiment_key: experiment_key,
|
|
1038
1075
|
variation_key: variation_key
|
|
1039
1076
|
)
|
|
@@ -1090,7 +1127,8 @@ module Optimizely
|
|
|
1090
1127
|
end
|
|
1091
1128
|
|
|
1092
1129
|
user_context = OptimizelyUserContext.new(self, user_id, attributes, identify: false)
|
|
1093
|
-
|
|
1130
|
+
decision_result = @decision_service.get_variation_for_feature(config, feature_flag, user_context)
|
|
1131
|
+
decision = decision_result.decision
|
|
1094
1132
|
variation = decision ? decision['variation'] : nil
|
|
1095
1133
|
feature_enabled = variation ? variation['featureEnabled'] : false
|
|
1096
1134
|
|
|
@@ -1108,7 +1146,7 @@ module Optimizely
|
|
|
1108
1146
|
|
|
1109
1147
|
@notification_center.send_notifications(
|
|
1110
1148
|
NotificationCenter::NOTIFICATION_TYPES[:DECISION],
|
|
1111
|
-
Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_VARIABLE'], user_id,
|
|
1149
|
+
Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_VARIABLE'], user_id, attributes || {},
|
|
1112
1150
|
feature_key: feature_flag_key,
|
|
1113
1151
|
feature_enabled: feature_enabled,
|
|
1114
1152
|
source: source_string,
|
|
@@ -1208,7 +1246,7 @@ module Optimizely
|
|
|
1208
1246
|
raise InvalidInputError, 'event_dispatcher'
|
|
1209
1247
|
end
|
|
1210
1248
|
|
|
1211
|
-
def send_impression(config, experiment, variation_key, flag_key, rule_key, enabled, rule_type, user_id, attributes = nil)
|
|
1249
|
+
def send_impression(config, experiment, variation_key, flag_key, rule_key, enabled, rule_type, user_id, attributes = nil, cmab_uuid = nil)
|
|
1212
1250
|
if experiment.nil?
|
|
1213
1251
|
experiment = {
|
|
1214
1252
|
'id' => '',
|
|
@@ -1240,6 +1278,7 @@ module Optimizely
|
|
|
1240
1278
|
variation_key: variation_key,
|
|
1241
1279
|
enabled: enabled
|
|
1242
1280
|
}
|
|
1281
|
+
metadata[:cmab_uuid] = cmab_uuid unless cmab_uuid.nil?
|
|
1243
1282
|
|
|
1244
1283
|
user_event = UserEventFactory.create_impression_event(config, experiment, variation_id, metadata, user_id, attributes)
|
|
1245
1284
|
@event_processor.process(user_event)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: optimizely-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Optimizely
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -134,6 +134,8 @@ files:
|
|
|
134
134
|
- lib/optimizely.rb
|
|
135
135
|
- lib/optimizely/audience.rb
|
|
136
136
|
- lib/optimizely/bucketer.rb
|
|
137
|
+
- lib/optimizely/cmab/cmab_client.rb
|
|
138
|
+
- lib/optimizely/cmab/cmab_service.rb
|
|
137
139
|
- lib/optimizely/condition_tree_evaluator.rb
|
|
138
140
|
- lib/optimizely/config/datafile_project_config.rb
|
|
139
141
|
- lib/optimizely/config/proxy_config.rb
|