optimizely-sdk 3.4.0 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
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],