optimizely-sdk 4.0.1 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/lib/optimizely/audience.rb +7 -7
  4. data/lib/optimizely/bucketer.rb +2 -2
  5. data/lib/optimizely/config/datafile_project_config.rb +58 -39
  6. data/lib/optimizely/config_manager/http_project_config_manager.rb +20 -10
  7. data/lib/optimizely/config_manager/project_config_manager.rb +2 -1
  8. data/lib/optimizely/config_manager/static_project_config_manager.rb +5 -3
  9. data/lib/optimizely/event/event_factory.rb +2 -2
  10. data/lib/optimizely/event_builder.rb +13 -13
  11. data/lib/optimizely/event_dispatcher.rb +2 -4
  12. data/lib/optimizely/exceptions.rb +69 -11
  13. data/lib/optimizely/helpers/constants.rb +45 -1
  14. data/lib/optimizely/helpers/http_utils.rb +3 -0
  15. data/lib/optimizely/helpers/sdk_settings.rb +61 -0
  16. data/lib/optimizely/helpers/validator.rb +54 -1
  17. data/lib/optimizely/notification_center_registry.rb +71 -0
  18. data/lib/optimizely/odp/lru_cache.rb +114 -0
  19. data/lib/optimizely/odp/odp_config.rb +102 -0
  20. data/lib/optimizely/odp/odp_event.rb +75 -0
  21. data/lib/optimizely/odp/odp_event_api_manager.rb +70 -0
  22. data/lib/optimizely/odp/odp_event_manager.rb +286 -0
  23. data/lib/optimizely/odp/odp_manager.rb +159 -0
  24. data/lib/optimizely/odp/odp_segment_api_manager.rb +122 -0
  25. data/lib/optimizely/odp/odp_segment_manager.rb +97 -0
  26. data/lib/optimizely/optimizely_config.rb +4 -2
  27. data/lib/optimizely/optimizely_factory.rb +17 -14
  28. data/lib/optimizely/optimizely_user_context.rb +40 -6
  29. data/lib/optimizely/user_condition_evaluator.rb +1 -1
  30. data/lib/optimizely/version.rb +2 -2
  31. data/lib/optimizely.rb +155 -23
  32. metadata +15 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6c3ed7012fac749fdca7c3c328eed7157fbc506f7d69ccebbae2d8064f23efd
4
- data.tar.gz: 0e3e8c5b066fed0afbb542a41441e84da94fa2c92aa323e995eb59a1323d0e45
3
+ metadata.gz: 7249ff9bec6f12c00a9a873df03e6f91c9692bcbe2388e87ea9a6a6f1472fbf5
4
+ data.tar.gz: 902fa025d32f1224833fa25f13158817ff3129244819a170ee3d907812290709
5
5
  SHA512:
6
- metadata.gz: d04f0b497e127336af09e8ddf071767614e2a8c8bb63ab5e6681e18faefcc105864633ca667d79706637b2c49d5e1324ef63d225fb7e4f7388fce785be443854
7
- data.tar.gz: 35d476afe2f1ff9a8d10367a59cd153485dd169324bd8f4f43faba56939bfbd85489160ca5453856bbaa16302357eebec603a9821e2551bb92df7290342d3deb
6
+ metadata.gz: b96a27927fd78a3d789b9edbb2c8488ba499c4f00220f08f9d9463691f38580db04f5dc6f7278873301a6c7217c54a9cf4f38d400b9feba196b7df70ec173e33
7
+ data.tar.gz: 368c9b5f096c8773c4bac487b8d6198ade424dde1ea6295449eaee3ecf5c0b4f7e2ab6b88a8bac4ab5ed2a2dce31f3ca24ef8c8596faae34cdae18aebfcdab79
data/LICENSE CHANGED
@@ -187,7 +187,7 @@
187
187
  same "printed page" as the copyright notice for easier
188
188
  identification within third-party archives.
189
189
 
190
- Copyright 2016, Optimizely and contributors
190
+ © Optimizely 2016
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
193
193
  you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2017, 2019-2020, Optimizely and contributors
4
+ # Copyright 2016-2017, 2019-2020, 2023, 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.
@@ -59,7 +59,7 @@ module Optimizely
59
59
  user_condition_evaluator = UserConditionEvaluator.new(user_context, logger)
60
60
 
61
61
  evaluate_user_conditions = lambda do |condition|
62
- return user_condition_evaluator.evaluate(condition)
62
+ user_condition_evaluator.evaluate(condition)
63
63
  end
64
64
 
65
65
  evaluate_audience = lambda do |audience_id|
@@ -112,12 +112,12 @@ module Optimizely
112
112
  # Returns array of segment names.
113
113
  segments = []
114
114
 
115
- conditions.each do |condition|
116
- case condition
117
- when Array
115
+ case conditions
116
+ when Hash
117
+ segments.push(conditions['value']) if conditions.fetch('match', nil) == 'qualified'
118
+ when Array
119
+ conditions.each do |condition|
118
120
  segments.concat @parse_segments.call(condition)
119
- when Hash
120
- segments.push(condition['value']) if condition.fetch('match', nil) == 'qualified'
121
121
  end
122
122
  end
123
123
 
@@ -110,8 +110,8 @@ module Optimizely
110
110
  # parent_id - String entity ID to use for bucketing ID
111
111
  # traffic_allocations - Array of traffic allocations
112
112
  #
113
- # Returns and array of two values where first value is the entity ID corresponding to the provided bucket value
114
- # or nil if no match is found. The second value contains the array of reasons stating how the deicision was taken
113
+ # Returns an array of two values where first value is the entity ID corresponding to the provided bucket value
114
+ # or nil if no match is found. The second value contains the array of reasons stating how the decision was taken
115
115
  decide_reasons = []
116
116
  bucketing_key = format(BUCKETING_ID_TEMPLATE, bucketing_id: bucketing_id, entity_id: parent_id)
117
117
  bucket_value = generate_bucket_value(bucketing_key)
@@ -93,7 +93,7 @@ module Optimizely
93
93
  @experiment_key_map = generate_key_map(@experiments, 'key')
94
94
  @experiment_id_map = generate_key_map(@experiments, 'id')
95
95
  @audience_id_map = generate_key_map(@audiences, 'id')
96
- @integration_key_map = generate_key_map(@integrations, 'key')
96
+ @integration_key_map = generate_key_map(@integrations, 'key', first_value: true)
97
97
  @audience_id_map = @audience_id_map.merge(generate_key_map(@typed_audiences, 'id')) unless @typed_audiences.empty?
98
98
  @variation_id_map = {}
99
99
  @variation_key_map = {}
@@ -184,20 +184,19 @@ module Optimizely
184
184
  # skip_json_validation - Optional boolean param which allows skipping JSON schema
185
185
  # validation upon object invocation. By default JSON schema validation will be performed.
186
186
  # Returns instance of DatafileProjectConfig, nil otherwise.
187
+ logger ||= SimpleLogger.new
187
188
  if !skip_json_validation && !Helpers::Validator.datafile_valid?(datafile)
188
- default_logger = SimpleLogger.new
189
- default_logger.log(Logger::ERROR, InvalidInputError.new('datafile').message)
189
+ logger.log(Logger::ERROR, InvalidInputError.new('datafile').message)
190
190
  return nil
191
191
  end
192
192
 
193
193
  begin
194
194
  config = new(datafile, logger, error_handler)
195
195
  rescue StandardError => e
196
- default_logger = SimpleLogger.new
197
196
  error_to_handle = e.instance_of?(InvalidDatafileVersionError) ? e : InvalidInputError.new('datafile')
198
197
  error_msg = error_to_handle.message
199
198
 
200
- default_logger.log(Logger::ERROR, error_msg)
199
+ logger.log(Logger::ERROR, error_msg)
201
200
  error_handler.handle_error error_to_handle
202
201
  return nil
203
202
  end
@@ -224,8 +223,9 @@ module Optimizely
224
223
  experiment = @experiment_key_map[experiment_key]
225
224
  return experiment if experiment
226
225
 
227
- @logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
228
- @error_handler.handle_error InvalidExperimentError
226
+ invalid_experiment_error = InvalidExperimentError.new(experiment_key: experiment_key)
227
+ @logger.log Logger::ERROR, invalid_experiment_error.message
228
+ @error_handler.handle_error invalid_experiment_error
229
229
  nil
230
230
  end
231
231
 
@@ -239,8 +239,9 @@ module Optimizely
239
239
  experiment = @experiment_id_map[experiment_id]
240
240
  return experiment if experiment
241
241
 
242
- @logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
243
- @error_handler.handle_error InvalidExperimentError
242
+ invalid_experiment_error = InvalidExperimentError.new(experiment_id: experiment_id)
243
+ @logger.log Logger::ERROR, invalid_experiment_error.message
244
+ @error_handler.handle_error invalid_experiment_error
244
245
  nil
245
246
  end
246
247
 
@@ -254,8 +255,9 @@ module Optimizely
254
255
  experiment = @experiment_id_map[experiment_id]
255
256
  return experiment['key'] unless experiment.nil?
256
257
 
257
- @logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
258
- @error_handler.handle_error InvalidExperimentError
258
+ invalid_experiment_error = InvalidExperimentError.new(experiment_id: experiment_id)
259
+ @logger.log Logger::ERROR, invalid_experiment_error.message
260
+ @error_handler.handle_error invalid_experiment_error
259
261
  nil
260
262
  end
261
263
 
@@ -269,8 +271,9 @@ module Optimizely
269
271
  event = @event_key_map[event_key]
270
272
  return event if event
271
273
 
272
- @logger.log Logger::ERROR, "Event '#{event_key}' is not in datafile."
273
- @error_handler.handle_error InvalidEventError
274
+ invalid_event_error = InvalidEventError.new(event_key)
275
+ @logger.log Logger::ERROR, invalid_event_error.message
276
+ @error_handler.handle_error invalid_event_error
274
277
  nil
275
278
  end
276
279
 
@@ -284,8 +287,9 @@ module Optimizely
284
287
  audience = @audience_id_map[audience_id]
285
288
  return audience if audience
286
289
 
287
- @logger.log Logger::ERROR, "Audience '#{audience_id}' is not in datafile."
288
- @error_handler.handle_error InvalidAudienceError
290
+ invalid_audience_error = InvalidAudienceError.new(audience_id)
291
+ @logger.log Logger::ERROR, invalid_audience_error.message
292
+ @error_handler.handle_error invalid_audience_error
289
293
  nil
290
294
  end
291
295
 
@@ -309,13 +313,15 @@ module Optimizely
309
313
  variation = variation_id_map[variation_id]
310
314
  return variation if variation
311
315
 
312
- @logger.log Logger::ERROR, "Variation id '#{variation_id}' is not in datafile."
313
- @error_handler.handle_error InvalidVariationError
316
+ invalid_variation_error = InvalidVariationError.new(variation_id: variation_id)
317
+ @logger.log Logger::ERROR, invalid_variation_error.message
318
+ @error_handler.handle_error invalid_variation_error
314
319
  return nil
315
320
  end
316
321
 
317
- @logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
318
- @error_handler.handle_error InvalidExperimentError
322
+ invalid_experiment_error = InvalidExperimentError.new(experiment_key: experiment_key)
323
+ @logger.log Logger::ERROR, invalid_experiment_error.message
324
+ @error_handler.handle_error invalid_experiment_error
319
325
  nil
320
326
  end
321
327
 
@@ -332,13 +338,15 @@ module Optimizely
332
338
  variation = variation_id_map_by_experiment_id[variation_id]
333
339
  return variation if variation
334
340
 
335
- @logger.log Logger::ERROR, "Variation id '#{variation_id}' is not in datafile."
336
- @error_handler.handle_error InvalidVariationError
341
+ invalid_variation_error = InvalidVariationError.new(variation_id: variation_id)
342
+ @logger.log Logger::ERROR, invalid_variation_error.message
343
+ @error_handler.handle_error invalid_variation_error
337
344
  return nil
338
345
  end
339
346
 
340
- @logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
341
- @error_handler.handle_error InvalidExperimentError
347
+ invalid_experiment_error = InvalidExperimentError.new(experiment_id: experiment_id)
348
+ @logger.log Logger::ERROR, invalid_experiment_error.message
349
+ @error_handler.handle_error invalid_experiment_error
342
350
  nil
343
351
  end
344
352
 
@@ -355,13 +363,15 @@ module Optimizely
355
363
  variation = variation_key_map[variation_key]
356
364
  return variation['id'] if variation
357
365
 
358
- @logger.log Logger::ERROR, "Variation key '#{variation_key}' is not in datafile."
359
- @error_handler.handle_error InvalidVariationError
366
+ invalid_variation_error = InvalidVariationError.new(variation_key: variation_key)
367
+ @logger.log Logger::ERROR, invalid_variation_error.message
368
+ @error_handler.handle_error invalid_variation_error
360
369
  return nil
361
370
  end
362
371
 
363
- @logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
364
- @error_handler.handle_error InvalidExperimentError
372
+ invalid_experiment_error = InvalidExperimentError.new(experiment_id: experiment_id)
373
+ @logger.log Logger::ERROR, invalid_experiment_error.message
374
+ @error_handler.handle_error invalid_experiment_error
365
375
  nil
366
376
  end
367
377
 
@@ -378,13 +388,15 @@ module Optimizely
378
388
  variation = variation_key_map[variation_key]
379
389
  return variation['id'] if variation
380
390
 
381
- @logger.log Logger::ERROR, "Variation key '#{variation_key}' is not in datafile."
382
- @error_handler.handle_error InvalidVariationError
391
+ invalid_variation_error = InvalidVariationError.new(variation_key: variation_key)
392
+ @logger.log Logger::ERROR, invalid_variation_error.message
393
+ @error_handler.handle_error invalid_variation_error
383
394
  return nil
384
395
  end
385
396
 
386
- @logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
387
- @error_handler.handle_error InvalidExperimentError
397
+ invalid_experiment_error = InvalidExperimentError.new(experiment_key: experiment_key)
398
+ @logger.log Logger::ERROR, invalid_experiment_error.message
399
+ @error_handler.handle_error invalid_experiment_error
388
400
  nil
389
401
  end
390
402
 
@@ -398,8 +410,9 @@ module Optimizely
398
410
  experiment = @experiment_id_map[experiment_id]
399
411
  return experiment['forcedVariations'] if experiment
400
412
 
401
- @logger.log Logger::ERROR, "Experiment ID '#{experiment_id}' is not in datafile."
402
- @error_handler.handle_error InvalidExperimentError
413
+ invalid_experiment_error = InvalidExperimentError.new(experiment_id: experiment_id)
414
+ @logger.log Logger::ERROR, invalid_experiment_error.message
415
+ @error_handler.handle_error invalid_experiment_error
403
416
  end
404
417
 
405
418
  def get_attribute_id(attribute_key)
@@ -421,8 +434,9 @@ module Optimizely
421
434
  end
422
435
  return attribute_key if has_reserved_prefix
423
436
 
424
- @logger.log Logger::ERROR, "Attribute key '#{attribute_key}' is not in datafile."
425
- @error_handler.handle_error InvalidAttributeError
437
+ invalid_attribute_error = InvalidAttributeError.new(attribute_key)
438
+ @logger.log Logger::ERROR, invalid_attribute_error.message
439
+ @error_handler.handle_error invalid_attribute_error
426
440
  nil
427
441
  end
428
442
 
@@ -440,8 +454,9 @@ module Optimizely
440
454
  variation = variation_id_map[variation_id]
441
455
  return true if variation
442
456
 
443
- @logger.log Logger::ERROR, "Variation ID '#{variation_id}' is not in datafile."
444
- @error_handler.handle_error InvalidVariationError
457
+ invalid_variation_error = InvalidVariationError.new(variation_id: variation_id)
458
+ @logger.log Logger::ERROR, invalid_variation_error.message
459
+ @error_handler.handle_error invalid_variation_error
445
460
  end
446
461
 
447
462
  false
@@ -525,15 +540,19 @@ module Optimizely
525
540
  flag_variation_map
526
541
  end
527
542
 
528
- def generate_key_map(array, key)
543
+ def generate_key_map(array, key, first_value: false)
529
544
  # Helper method to generate map from key to hash in array of hashes
530
545
  #
531
546
  # array - Array consisting of hash
532
547
  # key - Key in each hash which will be key in the map
548
+ # first_value - Determines which value to save if there are duplicate keys. By default the last instance of the key
549
+ # will be saved. Set to true to save the first key/value encountered.
533
550
  #
534
551
  # Returns map mapping key to hash
535
552
 
536
- Hash[array.map { |obj| [obj[key], obj] }]
553
+ array
554
+ .group_by { |obj| obj[key] }
555
+ .transform_values { |group| first_value ? group.first : group.last }
537
556
  end
538
557
  end
539
558
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2019-2020, 2022, Optimizely and contributors
4
+ # Copyright 2019-2020, 2022-2023, 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.
@@ -33,12 +33,12 @@ module Optimizely
33
33
  class HTTPProjectConfigManager < ProjectConfigManager
34
34
  # Config manager that polls for the datafile and updated ProjectConfig based on an update interval.
35
35
 
36
- attr_reader :stopped
36
+ attr_reader :stopped, :sdk_key
37
37
 
38
38
  # Initialize config manager. One of sdk_key or url has to be set to be able to use.
39
39
  #
40
- # sdk_key - Optional string uniquely identifying the datafile. It's required unless a URL is passed in.
41
- # datafile: Optional JSON string representing the project.
40
+ # sdk_key - Optional string uniquely identifying the datafile. It's required unless a datafile with sdk_key is passed in.
41
+ # datafile - Optional JSON string representing the project. If nil, sdk_key is required.
42
42
  # polling_interval - Optional floating point number representing time interval in seconds
43
43
  # at which to request datafile and set ProjectConfig.
44
44
  # blocking_timeout - Optional Time in seconds to block the config call until config object has been initialized.
@@ -83,6 +83,10 @@ module Optimizely
83
83
  @notification_center = notification_center.is_a?(Optimizely::NotificationCenter) ? notification_center : NotificationCenter.new(@logger, @error_handler)
84
84
  @optimizely_config = nil
85
85
  @config = datafile.nil? ? nil : DatafileProjectConfig.create(datafile, @logger, @error_handler, @skip_json_validation)
86
+ @sdk_key = sdk_key || @config&.sdk_key
87
+
88
+ raise MissingSdkKeyError if @sdk_key.nil?
89
+
86
90
  @mutex = Mutex.new
87
91
  @resource = ConditionVariable.new
88
92
  @async_scheduler = AsyncScheduler.new(method(:fetch_datafile_config), @polling_interval, auto_update, @logger)
@@ -98,11 +102,6 @@ module Optimizely
98
102
  end
99
103
 
100
104
  def start!
101
- if @stopped
102
- @logger.log(Logger::WARN, 'Not starting. Already stopped.')
103
- return
104
- end
105
-
106
105
  @async_scheduler.start!
107
106
  @stopped = false
108
107
  end
@@ -142,7 +141,7 @@ module Optimizely
142
141
  end
143
142
 
144
143
  def optimizely_config
145
- @optimizely_config = OptimizelyConfig.new(@config).config if @optimizely_config.nil?
144
+ @optimizely_config = OptimizelyConfig.new(@config, @logger).config if @optimizely_config.nil?
146
145
 
147
146
  @optimizely_config
148
147
  end
@@ -222,6 +221,10 @@ module Optimizely
222
221
 
223
222
  @notification_center.send_notifications(NotificationCenter::NOTIFICATION_TYPES[:OPTIMIZELY_CONFIG_UPDATE])
224
223
 
224
+ NotificationCenterRegistry
225
+ .get_notification_center(@sdk_key, @logger)
226
+ &.send_notifications(NotificationCenter::NOTIFICATION_TYPES[:OPTIMIZELY_CONFIG_UPDATE])
227
+
225
228
  @logger.log(Logger::DEBUG, 'Received new datafile and updated config. ' \
226
229
  "Old revision number: #{previous_revision}. New revision number: #{@config.revision}.")
227
230
  end
@@ -260,6 +263,13 @@ module Optimizely
260
263
  return
261
264
  end
262
265
 
266
+ if polling_interval < 30
267
+ @logger.log(
268
+ Logger::WARN,
269
+ 'Polling intervals below 30 seconds are not recommended.'
270
+ )
271
+ end
272
+
263
273
  @polling_interval = polling_interval
264
274
  end
265
275
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2019, Optimizely and contributors
4
+ # Copyright 2019, 2023, 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.
@@ -20,5 +20,6 @@ module Optimizely
20
20
  # Interface for fetching ProjectConfig instance.
21
21
 
22
22
  def config; end
23
+ def sdk_key; end
23
24
  end
24
25
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2019-2020, 2022, Optimizely and contributors
4
+ # Copyright 2019-2020, 2022-2023, 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.
@@ -23,7 +23,7 @@ require_relative 'project_config_manager'
23
23
  module Optimizely
24
24
  class StaticProjectConfigManager < ProjectConfigManager
25
25
  # Implementation of ProjectConfigManager interface.
26
- attr_reader :config
26
+ attr_reader :config, :sdk_key
27
27
 
28
28
  def initialize(datafile, logger, error_handler, skip_json_validation)
29
29
  # Looks up and sets datafile and config based on response body.
@@ -41,11 +41,13 @@ module Optimizely
41
41
  error_handler,
42
42
  skip_json_validation
43
43
  )
44
+ @logger = logger
45
+ @sdk_key = @config&.sdk_key
44
46
  @optimizely_config = nil
45
47
  end
46
48
 
47
49
  def optimizely_config
48
- @optimizely_config = OptimizelyConfig.new(@config).config if @optimizely_config.nil?
50
+ @optimizely_config = OptimizelyConfig.new(@config, @logger).config if @optimizely_config.nil?
49
51
 
50
52
  @optimizely_config
51
53
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2019-2020, 2022, Optimizely and contributors
4
+ # Copyright 2019-2020, 2022-2023, 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.
@@ -72,7 +72,7 @@ module Optimizely
72
72
 
73
73
  def build_attribute_list(user_attributes, project_config)
74
74
  visitor_attributes = []
75
- user_attributes&.keys&.each do |attribute_key|
75
+ user_attributes&.each_key do |attribute_key|
76
76
  # Omit attribute values that are not supported by the log endpoint.
77
77
  attribute_value = user_attributes[attribute_key]
78
78
  next unless Helpers::Validator.attribute_valid?(attribute_key, attribute_value)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2019, 2022, Optimizely and contributors
4
+ # Copyright 2016-2019, 2022-2023, 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.
@@ -62,20 +62,20 @@ module Optimizely
62
62
 
63
63
  visitor_attributes = []
64
64
 
65
- attributes&.keys&.each do |attribute_key|
65
+ attributes&.each_key do |attribute_key|
66
66
  # Omit attribute values that are not supported by the log endpoint.
67
67
  attribute_value = attributes[attribute_key]
68
- if Helpers::Validator.attribute_valid?(attribute_key, attribute_value)
69
- attribute_id = project_config.get_attribute_id attribute_key
70
- if attribute_id
71
- visitor_attributes.push(
72
- entity_id: attribute_id,
73
- key: attribute_key,
74
- type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
75
- value: attribute_value
76
- )
77
- end
78
- end
68
+ next unless Helpers::Validator.attribute_valid?(attribute_key, attribute_value)
69
+
70
+ attribute_id = project_config.get_attribute_id attribute_key
71
+ next unless attribute_id
72
+
73
+ visitor_attributes.push(
74
+ entity_id: attribute_id,
75
+ key: attribute_key,
76
+ type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
77
+ value: attribute_value
78
+ )
79
79
  end
80
80
  # Append Bot Filtering Attribute
81
81
  if project_config.bot_filtering == true || project_config.bot_filtering == false
@@ -17,6 +17,7 @@
17
17
  #
18
18
  require_relative 'exceptions'
19
19
  require_relative 'helpers/http_utils'
20
+ require_relative 'helpers/constants'
20
21
 
21
22
  module Optimizely
22
23
  class NoOpEventDispatcher
@@ -26,9 +27,6 @@ module Optimizely
26
27
  end
27
28
 
28
29
  class EventDispatcher
29
- # @api constants
30
- REQUEST_TIMEOUT = 10
31
-
32
30
  def initialize(logger: nil, error_handler: nil, proxy_config: nil)
33
31
  @logger = logger || NoOpLogger.new
34
32
  @error_handler = error_handler || NoOpErrorHandler.new
@@ -40,7 +38,7 @@ module Optimizely
40
38
  # @param event - Event object
41
39
  def dispatch_event(event)
42
40
  response = Helpers::HttpUtils.make_request(
43
- event.url, event.http_verb, event.params.to_json, event.headers, REQUEST_TIMEOUT, @proxy_config
41
+ event.url, event.http_verb, event.params.to_json, event.headers, Helpers::Constants::EVENT_DISPATCH_CONFIG[:REQUEST_TIMEOUT], @proxy_config
44
42
  )
45
43
 
46
44
  error_msg = "Event failed to dispatch with response code: #{response.code}"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2020, 2022, Optimizely and contributors
4
+ # Copyright 2016-2020, 2022-2023, 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.
@@ -25,19 +25,45 @@ module Optimizely
25
25
  end
26
26
  end
27
27
 
28
+ class HTTPUriError < Error
29
+ # Raised when a provided URI is invalid.
30
+ def initialize(msg = 'Provided URI was invalid.')
31
+ super
32
+ end
33
+ end
34
+
35
+ class MissingSdkKeyError < Error
36
+ # Raised when a provided URI is invalid.
37
+ def initialize(msg = 'SDK key not provided/cannot be found in the datafile.')
38
+ super
39
+ end
40
+ end
41
+
28
42
  class InvalidAudienceError < Error
29
43
  # Raised when an invalid audience is provided
30
44
 
31
- def initialize(msg = 'Provided audience is not in datafile.')
32
- super
45
+ attr_reader :audience_id
46
+
47
+ def initialize(audience_id)
48
+ raise ArgumentError, 'audience_id must be provided' if audience_id.nil?
49
+
50
+ super("Audience id '#{audience_id}' is not in datafile.")
51
+
52
+ @audience_id = audience_id
33
53
  end
34
54
  end
35
55
 
36
56
  class InvalidAttributeError < Error
37
57
  # Raised when an invalid attribute is provided
38
58
 
39
- def initialize(msg = 'Provided attribute is not in datafile.')
40
- super
59
+ attr_reader :attribute_key
60
+
61
+ def initialize(attribute_key)
62
+ raise ArgumentError, 'attribute_key must be provided' if attribute_key.nil?
63
+
64
+ super("Attribute key '#{attribute_key}' is not in datafile.")
65
+
66
+ @attribute_key = attribute_key
41
67
  end
42
68
  end
43
69
 
@@ -60,24 +86,56 @@ module Optimizely
60
86
  class InvalidExperimentError < Error
61
87
  # Raised when an invalid experiment key is provided
62
88
 
63
- def initialize(msg = 'Provided experiment is not in datafile.')
64
- super
89
+ attr_reader :experiment_id, :experiment_key
90
+
91
+ def initialize(experiment_id: nil, experiment_key: nil)
92
+ raise ArgumentError, 'Either experiment_id or experiment_key must be provided.' if experiment_id.nil? && experiment_key.nil?
93
+ raise ArgumentError, 'Cannot provide both experiment_id and experiment_key.' if !experiment_id.nil? && !experiment_key.nil?
94
+
95
+ if experiment_id.nil?
96
+ @experiment_key = experiment_key
97
+ identifier = "key '#{@experiment_key}'"
98
+ else
99
+ @experiment_id = experiment_id
100
+ identifier = "id '#{@experiment_id}'"
101
+ end
102
+
103
+ super("Experiment #{identifier} is not in datafile.")
65
104
  end
66
105
  end
67
106
 
68
107
  class InvalidEventError < Error
69
108
  # Raised when an invalid event key is provided
70
109
 
71
- def initialize(msg = 'Provided event is not in datafile.')
72
- super
110
+ attr_reader :event_key
111
+
112
+ def initialize(event_key)
113
+ raise ArgumentError, 'event_key must be provided.' if event_key.nil?
114
+
115
+ super("Event key '#{event_key}' is not in datafile.")
116
+
117
+ @event_key = event_key
73
118
  end
74
119
  end
75
120
 
76
121
  class InvalidVariationError < Error
77
122
  # Raised when an invalid variation key or ID is provided
78
123
 
79
- def initialize(msg = 'Provided variation is not in datafile.')
80
- super
124
+ attr_reader :variation_id, :variation_key
125
+
126
+ def initialize(variation_id: nil, variation_key: nil)
127
+ raise ArgumentError, 'Either variation_id or variation_key must be provided.' if variation_id.nil? && variation_key.nil?
128
+ raise ArgumentError, 'Cannot provide both variation_id and variation_key.' if !variation_id.nil? && !variation_key.nil?
129
+
130
+ if variation_id.nil?
131
+ identifier = "key '#{variation_key}'"
132
+ @variation_key = variation_key
133
+ else
134
+ identifier = "id '#{variation_id}'"
135
+ @variation_id = variation_id
136
+ end
137
+
138
+ super("Variation #{identifier} is not in datafile.")
81
139
  end
82
140
  end
83
141