optimizely-sdk 3.4.0 → 3.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00fc9a6599ce3b83c1696f788f5f4ad5e627c0063da459eb11353a7ae2872033
4
- data.tar.gz: 36a84eb5593ce921103a7c1829e4c0677d95f82a287f37347ec8de3484fad082
3
+ metadata.gz: 62e8da2296d75e8f0cad746e8eca25c2670bde6bac084a840c46266322dc378f
4
+ data.tar.gz: 67d25b1695a294616b6dc62c7fa1d822c5262f273626d8c3c5a701e78b8e118f
5
5
  SHA512:
6
- metadata.gz: 26f809dd552d54670e9d2ffc00bacbbb91ba509b7e5ce98a2cc720dacf495e5fb232f50286d0373f18144bac8ed2c5a540a3435423b03cea347b2eb449dbd053
7
- data.tar.gz: 67a123c84a058e5d398b39a3349266d9a2e484022c313ae63afc4f1ca97988a99e0285a9a4d6b42beb5df5c514e66cc4b54663f258bd602a80afbe8c28b175a2
6
+ metadata.gz: 8891366e7765049a20b552eff7bd070c51eacf635664096235de97c9858f039410f23a5a3e6a902aee2269008cc50c1a45b22026c831843962c3ca04aa9f35af
7
+ data.tar.gz: 358139de2ecbbe9c6b2f4473bee6e56b784d22ef0e4067fc20a7f0e85a8d7cf293a6fe3cf7b9f4f1dbcf17c463392f8cedad62a7ad651f2fb70ecaee59fd5391
data/lib/optimizely.rb CHANGED
@@ -19,6 +19,9 @@ require_relative 'optimizely/audience'
19
19
  require_relative 'optimizely/config/datafile_project_config'
20
20
  require_relative 'optimizely/config_manager/http_project_config_manager'
21
21
  require_relative 'optimizely/config_manager/static_project_config_manager'
22
+ require_relative 'optimizely/decide/optimizely_decide_option'
23
+ require_relative 'optimizely/decide/optimizely_decision'
24
+ require_relative 'optimizely/decide/optimizely_decision_message'
22
25
  require_relative 'optimizely/decision_service'
23
26
  require_relative 'optimizely/error_handler'
24
27
  require_relative 'optimizely/event_builder'
@@ -34,9 +37,12 @@ require_relative 'optimizely/helpers/variable_type'
34
37
  require_relative 'optimizely/logger'
35
38
  require_relative 'optimizely/notification_center'
36
39
  require_relative 'optimizely/optimizely_config'
40
+ require_relative 'optimizely/optimizely_user_context'
37
41
 
38
42
  module Optimizely
39
43
  class Project
44
+ include Optimizely::Decide
45
+
40
46
  attr_reader :notification_center
41
47
  # @api no-doc
42
48
  attr_reader :config_manager, :decision_service, :error_handler, :event_dispatcher,
@@ -67,12 +73,21 @@ module Optimizely
67
73
  sdk_key = nil,
68
74
  config_manager = nil,
69
75
  notification_center = nil,
70
- event_processor = nil
76
+ event_processor = nil,
77
+ default_decide_options = []
71
78
  )
72
79
  @logger = logger || NoOpLogger.new
73
80
  @error_handler = error_handler || NoOpErrorHandler.new
74
81
  @event_dispatcher = event_dispatcher || EventDispatcher.new(logger: @logger, error_handler: @error_handler)
75
82
  @user_profile_service = user_profile_service
83
+ @default_decide_options = []
84
+
85
+ if default_decide_options.is_a? Array
86
+ @default_decide_options = default_decide_options.clone
87
+ else
88
+ @logger.log(Logger::DEBUG, 'Provided default decide options is not an array.')
89
+ @default_decide_options = []
90
+ end
76
91
 
77
92
  begin
78
93
  validate_instantiation_options
@@ -107,6 +122,175 @@ module Optimizely
107
122
  end
108
123
  end
109
124
 
125
+ # Create a context of the user for which decision APIs will be called.
126
+ #
127
+ # A user context will be created successfully even when the SDK is not fully configured yet.
128
+ #
129
+ # @param user_id - The user ID to be used for bucketing.
130
+ # @param attributes - A Hash representing user attribute names and values.
131
+ #
132
+ # @return [OptimizelyUserContext] An OptimizelyUserContext associated with this OptimizelyClient.
133
+ # @return [nil] If user attributes are not in valid format.
134
+
135
+ def create_user_context(user_id, attributes = nil)
136
+ # We do not check for is_valid here as a user context can be created successfully
137
+ # even when the SDK is not fully configured.
138
+
139
+ # validate user_id
140
+ return nil unless Optimizely::Helpers::Validator.inputs_valid?(
141
+ {
142
+ user_id: user_id
143
+ }, @logger, Logger::ERROR
144
+ )
145
+
146
+ # validate attributes
147
+ return nil unless user_inputs_valid?(attributes)
148
+
149
+ user_context = OptimizelyUserContext.new(self, user_id, attributes)
150
+ user_context
151
+ end
152
+
153
+ def decide(user_context, key, decide_options = [])
154
+ # raising on user context as it is internal and not provided directly by the user.
155
+ raise if user_context.class != OptimizelyUserContext
156
+
157
+ reasons = []
158
+
159
+ # check if SDK is ready
160
+ unless is_valid
161
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('decide').message)
162
+ reasons.push(OptimizelyDecisionMessage::SDK_NOT_READY)
163
+ return OptimizelyDecision.new(flag_key: key, user_context: user_context, reasons: reasons)
164
+ end
165
+
166
+ # validate that key is a string
167
+ unless key.is_a?(String)
168
+ @logger.log(Logger::ERROR, 'Provided key is invalid')
169
+ reasons.push(format(OptimizelyDecisionMessage::FLAG_KEY_INVALID, key))
170
+ return OptimizelyDecision.new(flag_key: key, user_context: user_context, reasons: reasons)
171
+ end
172
+
173
+ # validate that key maps to a feature flag
174
+ config = project_config
175
+ feature_flag = config.get_feature_flag_from_key(key)
176
+ unless feature_flag
177
+ @logger.log(Logger::ERROR, "No feature flag was found for key '#{key}'.")
178
+ reasons.push(format(OptimizelyDecisionMessage::FLAG_KEY_INVALID, key))
179
+ return OptimizelyDecision.new(flag_key: key, user_context: user_context, reasons: reasons)
180
+ end
181
+
182
+ # merge decide_options and default_decide_options
183
+ if decide_options.is_a? Array
184
+ decide_options += @default_decide_options
185
+ else
186
+ @logger.log(Logger::DEBUG, 'Provided decide options is not an array. Using default decide options.')
187
+ decide_options = @default_decide_options
188
+ end
189
+
190
+ # Create Optimizely Decision Result.
191
+ user_id = user_context.user_id
192
+ attributes = user_context.user_attributes
193
+ variation_key = nil
194
+ feature_enabled = false
195
+ rule_key = nil
196
+ flag_key = key
197
+ all_variables = {}
198
+ decision_event_dispatched = false
199
+ experiment = nil
200
+ decision_source = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
201
+
202
+ decision, reasons_received = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes, decide_options)
203
+ reasons.push(*reasons_received)
204
+
205
+ # Send impression event if Decision came from a feature test and decide options doesn't include disableDecisionEvent
206
+ if decision.is_a?(Optimizely::DecisionService::Decision)
207
+ experiment = decision.experiment
208
+ rule_key = experiment['key']
209
+ variation = decision['variation']
210
+ variation_key = variation['key']
211
+ feature_enabled = variation['featureEnabled']
212
+ decision_source = decision.source
213
+ end
214
+
215
+ unless decide_options.include? OptimizelyDecideOption::DISABLE_DECISION_EVENT
216
+ if decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || config.send_flag_decisions
217
+ send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes)
218
+ decision_event_dispatched = true
219
+ end
220
+ end
221
+
222
+ # Generate all variables map if decide options doesn't include excludeVariables
223
+ unless decide_options.include? OptimizelyDecideOption::EXCLUDE_VARIABLES
224
+ feature_flag['variables'].each do |variable|
225
+ variable_value = get_feature_variable_for_variation(key, feature_enabled, variation, variable, user_id)
226
+ all_variables[variable['key']] = Helpers::VariableType.cast_value_to_type(variable_value, variable['type'], @logger)
227
+ end
228
+ end
229
+
230
+ should_include_reasons = decide_options.include? OptimizelyDecideOption::INCLUDE_REASONS
231
+
232
+ # Send notification
233
+ @notification_center.send_notifications(
234
+ NotificationCenter::NOTIFICATION_TYPES[:DECISION],
235
+ Helpers::Constants::DECISION_NOTIFICATION_TYPES['FLAG'],
236
+ user_id, (attributes || {}),
237
+ flag_key: flag_key,
238
+ enabled: feature_enabled,
239
+ variables: all_variables,
240
+ variation_key: variation_key,
241
+ rule_key: rule_key,
242
+ reasons: should_include_reasons ? reasons : [],
243
+ decision_event_dispatched: decision_event_dispatched
244
+ )
245
+
246
+ OptimizelyDecision.new(
247
+ variation_key: variation_key,
248
+ enabled: feature_enabled,
249
+ variables: all_variables,
250
+ rule_key: rule_key,
251
+ flag_key: flag_key,
252
+ user_context: user_context,
253
+ reasons: should_include_reasons ? reasons : []
254
+ )
255
+ end
256
+
257
+ def decide_all(user_context, decide_options = [])
258
+ # raising on user context as it is internal and not provided directly by the user.
259
+ raise if user_context.class != OptimizelyUserContext
260
+
261
+ # check if SDK is ready
262
+ unless is_valid
263
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('decide_all').message)
264
+ return {}
265
+ end
266
+
267
+ keys = []
268
+ project_config.feature_flags.each do |feature_flag|
269
+ keys.push(feature_flag['key'])
270
+ end
271
+ decide_for_keys(user_context, keys, decide_options)
272
+ end
273
+
274
+ def decide_for_keys(user_context, keys, decide_options = [])
275
+ # raising on user context as it is internal and not provided directly by the user.
276
+ raise if user_context.class != OptimizelyUserContext
277
+
278
+ # check if SDK is ready
279
+ unless is_valid
280
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('decide_for_keys').message)
281
+ return {}
282
+ end
283
+
284
+ enabled_flags_only = (!decide_options.nil? && (decide_options.include? OptimizelyDecideOption::ENABLED_FLAGS_ONLY)) || (@default_decide_options.include? OptimizelyDecideOption::ENABLED_FLAGS_ONLY)
285
+
286
+ decisions = {}
287
+ keys.each do |key|
288
+ decision = decide(user_context, key, decide_options)
289
+ decisions[key] = decision unless enabled_flags_only && !decision.enabled
290
+ end
291
+ decisions
292
+ end
293
+
110
294
  # Buckets visitor and sends impression event to Optimizely.
111
295
  #
112
296
  # @param experiment_key - Experiment which needs to be activated.
@@ -140,7 +324,10 @@ module Optimizely
140
324
 
141
325
  # Create and dispatch impression event
142
326
  experiment = config.get_experiment_from_key(experiment_key)
143
- send_impression(config, experiment, variation_key, user_id, attributes)
327
+ send_impression(
328
+ config, experiment, variation_key, '', experiment_key, true,
329
+ Optimizely::DecisionService::DECISION_SOURCES['EXPERIMENT'], user_id, attributes
330
+ )
144
331
 
145
332
  variation_key
146
333
  end
@@ -219,7 +406,7 @@ module Optimizely
219
406
  config = project_config
220
407
 
221
408
  forced_variation_key = nil
222
- forced_variation = @decision_service.get_forced_variation(config, experiment_key, user_id)
409
+ forced_variation, = @decision_service.get_forced_variation(config, experiment_key, user_id)
223
410
  forced_variation_key = forced_variation['key'] if forced_variation
224
411
 
225
412
  forced_variation_key
@@ -303,7 +490,7 @@ module Optimizely
303
490
  return false
304
491
  end
305
492
 
306
- decision = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
493
+ decision, = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
307
494
 
308
495
  feature_enabled = false
309
496
  source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
@@ -316,14 +503,23 @@ module Optimizely
316
503
  experiment_key: decision.experiment['key'],
317
504
  variation_key: variation['key']
318
505
  }
319
- # Send event if Decision came from an experiment.
320
- send_impression(config, decision.experiment, variation['key'], user_id, attributes)
321
- else
322
- @logger.log(Logger::DEBUG,
323
- "The user '#{user_id}' is not being experimented on in feature '#{feature_flag_key}'.")
506
+ # Send event if Decision came from a feature test.
507
+ send_impression(
508
+ config, decision.experiment, variation['key'], feature_flag_key, decision.experiment['key'], feature_enabled, source_string, user_id, attributes
509
+ )
510
+ elsif decision.source == Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] && config.send_flag_decisions
511
+ send_impression(
512
+ config, decision.experiment, variation['key'], feature_flag_key, decision.experiment['key'], feature_enabled, source_string, user_id, attributes
513
+ )
324
514
  end
325
515
  end
326
516
 
517
+ if decision.nil? && config.send_flag_decisions
518
+ send_impression(
519
+ config, nil, '', feature_flag_key, '', feature_enabled, source_string, user_id, attributes
520
+ )
521
+ end
522
+
327
523
  @notification_center.send_notifications(
328
524
  NotificationCenter::NOTIFICATION_TYPES[:DECISION],
329
525
  Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE'],
@@ -430,6 +626,32 @@ module Optimizely
430
626
  variable_value
431
627
  end
432
628
 
629
+ # Get the Json value of the specified variable in the feature flag in a Dict.
630
+ #
631
+ # @param feature_flag_key - String key of feature flag the variable belongs to
632
+ # @param variable_key - String key of variable for which we are getting the string value
633
+ # @param user_id - String user ID
634
+ # @param attributes - Hash representing visitor attributes and values which need to be recorded.
635
+ #
636
+ # @return [Dict] the Dict containing variable value.
637
+ # @return [nil] if the feature flag or variable are not found.
638
+
639
+ def get_feature_variable_json(feature_flag_key, variable_key, user_id, attributes = nil)
640
+ unless is_valid
641
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_json').message)
642
+ return nil
643
+ end
644
+ variable_value = get_feature_variable_for_type(
645
+ feature_flag_key,
646
+ variable_key,
647
+ Optimizely::Helpers::Constants::VARIABLE_TYPES['JSON'],
648
+ user_id,
649
+ attributes
650
+ )
651
+
652
+ variable_value
653
+ end
654
+
433
655
  # Get the Boolean value of the specified variable in the feature flag.
434
656
  #
435
657
  # @param feature_flag_key - String key of feature flag the variable belongs to
@@ -484,6 +706,71 @@ module Optimizely
484
706
  variable_value
485
707
  end
486
708
 
709
+ # Get values of all the variables in the feature flag and returns them in a Dict
710
+ #
711
+ # @param feature_flag_key - String key of feature flag
712
+ # @param user_id - String user ID
713
+ # @param attributes - Hash representing visitor attributes and values which need to be recorded.
714
+ #
715
+ # @return [Dict] the Dict containing all the varible values
716
+ # @return [nil] if the feature flag is not found.
717
+
718
+ def get_all_feature_variables(feature_flag_key, user_id, attributes = nil)
719
+ unless is_valid
720
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_all_feature_variables').message)
721
+ return nil
722
+ end
723
+
724
+ return nil unless Optimizely::Helpers::Validator.inputs_valid?(
725
+ {
726
+ feature_flag_key: feature_flag_key,
727
+ user_id: user_id
728
+ },
729
+ @logger, Logger::ERROR
730
+ )
731
+
732
+ return nil unless user_inputs_valid?(attributes)
733
+
734
+ config = project_config
735
+
736
+ feature_flag = config.get_feature_flag_from_key(feature_flag_key)
737
+ unless feature_flag
738
+ @logger.log(Logger::INFO, "No feature flag was found for key '#{feature_flag_key}'.")
739
+ return nil
740
+ end
741
+
742
+ decision, = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
743
+ variation = decision ? decision['variation'] : nil
744
+ feature_enabled = variation ? variation['featureEnabled'] : false
745
+ all_variables = {}
746
+
747
+ feature_flag['variables'].each do |variable|
748
+ variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
749
+ all_variables[variable['key']] = Helpers::VariableType.cast_value_to_type(variable_value, variable['type'], @logger)
750
+ end
751
+
752
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
753
+ if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
754
+ source_info = {
755
+ experiment_key: decision.experiment['key'],
756
+ variation_key: variation['key']
757
+ }
758
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
759
+ end
760
+
761
+ @notification_center.send_notifications(
762
+ NotificationCenter::NOTIFICATION_TYPES[:DECISION],
763
+ Helpers::Constants::DECISION_NOTIFICATION_TYPES['ALL_FEATURE_VARIABLES'], user_id, (attributes || {}),
764
+ feature_key: feature_flag_key,
765
+ feature_enabled: feature_enabled,
766
+ source: source_string,
767
+ variable_values: all_variables,
768
+ source_info: source_info || {}
769
+ )
770
+
771
+ all_variables
772
+ end
773
+
487
774
  # Get the Integer value of the specified variable in the feature flag.
488
775
  #
489
776
  # @param feature_flag_key - String key of feature flag the variable belongs to
@@ -592,7 +879,7 @@ module Optimizely
592
879
 
593
880
  return nil unless user_inputs_valid?(attributes)
594
881
 
595
- variation_id = @decision_service.get_variation(config, experiment_key, user_id, attributes)
882
+ variation_id, = @decision_service.get_variation(config, experiment_key, user_id, attributes)
596
883
  variation = config.get_variation_from_id(experiment_key, variation_id) unless variation_id.nil?
597
884
  variation_key = variation['key'] if variation
598
885
  decision_notification_type = if config.feature_experiment?(experiment['id'])
@@ -649,8 +936,6 @@ module Optimizely
649
936
  # Error message logged in DatafileProjectConfig- get_feature_flag_from_key
650
937
  return nil if variable.nil?
651
938
 
652
- feature_enabled = false
653
-
654
939
  # If variable_type is nil, set it equal to variable['type']
655
940
  variable_type ||= variable['type']
656
941
  # Returns nil if type differs
@@ -658,43 +943,24 @@ module Optimizely
658
943
  @logger.log(Logger::WARN,
659
944
  "Requested variable as type '#{variable_type}' but variable '#{variable_key}' is of type '#{variable['type']}'.")
660
945
  return nil
661
- else
662
- source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
663
- decision = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
664
- variable_value = variable['defaultValue']
665
- if decision
666
- variation = decision['variation']
667
- if decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
668
- source_info = {
669
- experiment_key: decision.experiment['key'],
670
- variation_key: variation['key']
671
- }
672
- source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
673
- end
674
- feature_enabled = variation['featureEnabled']
675
- if feature_enabled == true
676
- variation_variable_usages = config.variation_id_to_variable_usage_map[variation['id']]
677
- variable_id = variable['id']
678
- if variation_variable_usages&.key?(variable_id)
679
- variable_value = variation_variable_usages[variable_id]['value']
680
- @logger.log(Logger::INFO,
681
- "Got variable value '#{variable_value}' for variable '#{variable_key}' of feature flag '#{feature_flag_key}'.")
682
- else
683
- @logger.log(Logger::DEBUG,
684
- "Variable '#{variable_key}' is not used in variation '#{variation['key']}'. Returning the default variable value '#{variable_value}'.")
685
- end
686
- else
687
- @logger.log(Logger::DEBUG,
688
- "Feature '#{feature_flag_key}' for variation '#{variation['key']}' is not enabled. Returning the default variable value '#{variable_value}'.")
689
- end
690
- else
691
- @logger.log(Logger::INFO,
692
- "User '#{user_id}' was not bucketed into any variation for feature flag '#{feature_flag_key}'. Returning the default variable value '#{variable_value}'.")
693
- end
694
946
  end
695
947
 
948
+ decision, = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
949
+ variation = decision ? decision['variation'] : nil
950
+ feature_enabled = variation ? variation['featureEnabled'] : false
951
+
952
+ variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
696
953
  variable_value = Helpers::VariableType.cast_value_to_type(variable_value, variable_type, @logger)
697
954
 
955
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
956
+ if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
957
+ source_info = {
958
+ experiment_key: decision.experiment['key'],
959
+ variation_key: variation['key']
960
+ }
961
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
962
+ end
963
+
698
964
  @notification_center.send_notifications(
699
965
  NotificationCenter::NOTIFICATION_TYPES[:DECISION],
700
966
  Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_VARIABLE'], user_id, (attributes || {}),
@@ -710,6 +976,46 @@ module Optimizely
710
976
  variable_value
711
977
  end
712
978
 
979
+ def get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
980
+ # Helper method to get the non type-casted value for a variable attached to a
981
+ # feature flag. Returns appropriate variable value depending on whether there
982
+ # was a matching variation, feature was enabled or not or varible was part of the
983
+ # available variation or not. Also logs the appropriate message explaining how it
984
+ # evaluated the value of the variable.
985
+ #
986
+ # feature_flag_key - String key of feature flag the variable belongs to
987
+ # feature_enabled - Boolean indicating if feature is enabled or not
988
+ # variation - varition returned by decision service
989
+ # user_id - String user ID
990
+ #
991
+ # Returns string value of the variable.
992
+
993
+ config = project_config
994
+ variable_value = variable['defaultValue']
995
+ if variation
996
+ if feature_enabled == true
997
+ variation_variable_usages = config.variation_id_to_variable_usage_map[variation['id']]
998
+ variable_id = variable['id']
999
+ if variation_variable_usages&.key?(variable_id)
1000
+ variable_value = variation_variable_usages[variable_id]['value']
1001
+ @logger.log(Logger::INFO,
1002
+ "Got variable value '#{variable_value}' for variable '#{variable['key']}' of feature flag '#{feature_flag_key}'.")
1003
+ else
1004
+ @logger.log(Logger::DEBUG,
1005
+ "Variable value is not defined. Returning the default variable value '#{variable_value}' for variable '#{variable['key']}'.")
1006
+
1007
+ end
1008
+ else
1009
+ @logger.log(Logger::DEBUG,
1010
+ "Feature '#{feature_flag_key}' is not enabled for user '#{user_id}'. Returning the default variable value '#{variable_value}'.")
1011
+ end
1012
+ else
1013
+ @logger.log(Logger::INFO,
1014
+ "User '#{user_id}' was not bucketed into experiment or rollout for feature flag '#{feature_flag_key}'. Returning the default variable value '#{variable_value}'.")
1015
+ end
1016
+ variable_value
1017
+ end
1018
+
713
1019
  def user_inputs_valid?(attributes = nil, event_tags = nil)
714
1020
  # Helper method to validate user inputs.
715
1021
  #
@@ -757,15 +1063,43 @@ module Optimizely
757
1063
  raise InvalidInputError, 'event_dispatcher'
758
1064
  end
759
1065
 
760
- def send_impression(config, experiment, variation_key, user_id, attributes = nil)
1066
+ def send_impression(config, experiment, variation_key, flag_key, rule_key, enabled, rule_type, user_id, attributes = nil)
1067
+ if experiment.nil?
1068
+ experiment = {
1069
+ 'id' => '',
1070
+ 'key' => '',
1071
+ 'layerId' => '',
1072
+ 'status' => '',
1073
+ 'variations' => [],
1074
+ 'trafficAllocation' => [],
1075
+ 'audienceIds' => [],
1076
+ 'audienceConditions' => [],
1077
+ 'forcedVariations' => {}
1078
+ }
1079
+ end
1080
+
761
1081
  experiment_key = experiment['key']
762
- variation_id = config.get_variation_id_from_key(experiment_key, variation_key)
763
- user_event = UserEventFactory.create_impression_event(config, experiment, variation_id, user_id, attributes)
1082
+
1083
+ variation_id = ''
1084
+ variation_id = config.get_variation_id_from_key(experiment_key, variation_key) if experiment_key != ''
1085
+
1086
+ metadata = {
1087
+ flag_key: flag_key,
1088
+ rule_key: rule_key,
1089
+ rule_type: rule_type,
1090
+ variation_key: variation_key,
1091
+ enabled: enabled
1092
+ }
1093
+
1094
+ user_event = UserEventFactory.create_impression_event(config, experiment, variation_id, metadata, user_id, attributes)
764
1095
  @event_processor.process(user_event)
765
1096
  return unless @notification_center.notification_count(NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE]).positive?
766
1097
 
767
1098
  @logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_key}'.")
768
- variation = config.get_variation_from_id(experiment_key, variation_id)
1099
+
1100
+ experiment = nil if experiment_key == ''
1101
+ variation = nil
1102
+ variation = config.get_variation_from_id(experiment_key, variation_id) unless experiment.nil?
769
1103
  log_event = EventFactory.create_log_event(user_event, @logger)
770
1104
  @notification_center.send_notifications(
771
1105
  NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE],