optimizely-sdk 3.4.0 → 3.5.0.pre.beta

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: 033cd0357752fc0d52193944412f2a3029924e3ffdc650848924fe9d9a9ce6ee
4
+ data.tar.gz: 4e7949391d0a41b832c21f8835158345966edf63949256c15436321683927678
5
5
  SHA512:
6
- metadata.gz: 26f809dd552d54670e9d2ffc00bacbbb91ba509b7e5ce98a2cc720dacf495e5fb232f50286d0373f18144bac8ed2c5a540a3435423b03cea347b2eb449dbd053
7
- data.tar.gz: 67a123c84a058e5d398b39a3349266d9a2e484022c313ae63afc4f1ca97988a99e0285a9a4d6b42beb5df5c514e66cc4b54663f258bd602a80afbe8c28b175a2
6
+ metadata.gz: 79a434d80add6804f3722dc4d616d2533f263601a80a41d357ac1766d099f79eae6a6429eb945a198a0689ce6e2525fd6f44d2b308a1e7a0d470fe184d89e28b
7
+ data.tar.gz: 73735c47d0e2ed58a26de0b7a8fb25d8167f45e1f4535aeb90f4fe95fb0fd5733dbcf614efe4d4d8af4ee0b3d5080953224c2f2a8fee80380e521a97c1a310e8
@@ -430,6 +430,32 @@ module Optimizely
430
430
  variable_value
431
431
  end
432
432
 
433
+ # Get the Json value of the specified variable in the feature flag in a Dict.
434
+ #
435
+ # @param feature_flag_key - String key of feature flag the variable belongs to
436
+ # @param variable_key - String key of variable for which we are getting the string value
437
+ # @param user_id - String user ID
438
+ # @param attributes - Hash representing visitor attributes and values which need to be recorded.
439
+ #
440
+ # @return [Dict] the Dict containing variable value.
441
+ # @return [nil] if the feature flag or variable are not found.
442
+
443
+ def get_feature_variable_json(feature_flag_key, variable_key, user_id, attributes = nil)
444
+ unless is_valid
445
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_json').message)
446
+ return nil
447
+ end
448
+ variable_value = get_feature_variable_for_type(
449
+ feature_flag_key,
450
+ variable_key,
451
+ Optimizely::Helpers::Constants::VARIABLE_TYPES['JSON'],
452
+ user_id,
453
+ attributes
454
+ )
455
+
456
+ variable_value
457
+ end
458
+
433
459
  # Get the Boolean value of the specified variable in the feature flag.
434
460
  #
435
461
  # @param feature_flag_key - String key of feature flag the variable belongs to
@@ -484,6 +510,71 @@ module Optimizely
484
510
  variable_value
485
511
  end
486
512
 
513
+ # Get values of all the variables in the feature flag and returns them in a Dict
514
+ #
515
+ # @param feature_flag_key - String key of feature flag
516
+ # @param user_id - String user ID
517
+ # @param attributes - Hash representing visitor attributes and values which need to be recorded.
518
+ #
519
+ # @return [Dict] the Dict containing all the varible values
520
+ # @return [nil] if the feature flag is not found.
521
+
522
+ def get_all_feature_variables(feature_flag_key, user_id, attributes = nil)
523
+ unless is_valid
524
+ @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_all_feature_variables').message)
525
+ return nil
526
+ end
527
+
528
+ return nil unless Optimizely::Helpers::Validator.inputs_valid?(
529
+ {
530
+ feature_flag_key: feature_flag_key,
531
+ user_id: user_id
532
+ },
533
+ @logger, Logger::ERROR
534
+ )
535
+
536
+ return nil unless user_inputs_valid?(attributes)
537
+
538
+ config = project_config
539
+
540
+ feature_flag = config.get_feature_flag_from_key(feature_flag_key)
541
+ unless feature_flag
542
+ @logger.log(Logger::INFO, "No feature flag was found for key '#{feature_flag_key}'.")
543
+ return nil
544
+ end
545
+
546
+ decision = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
547
+ variation = decision ? decision['variation'] : nil
548
+ feature_enabled = variation ? variation['featureEnabled'] : false
549
+ all_variables = {}
550
+
551
+ feature_flag['variables'].each do |variable|
552
+ variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
553
+ all_variables[variable['key']] = Helpers::VariableType.cast_value_to_type(variable_value, variable['type'], @logger)
554
+ end
555
+
556
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
557
+ if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
558
+ source_info = {
559
+ experiment_key: decision.experiment['key'],
560
+ variation_key: variation['key']
561
+ }
562
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
563
+ end
564
+
565
+ @notification_center.send_notifications(
566
+ NotificationCenter::NOTIFICATION_TYPES[:DECISION],
567
+ Helpers::Constants::DECISION_NOTIFICATION_TYPES['ALL_FEATURE_VARIABLES'], user_id, (attributes || {}),
568
+ feature_key: feature_flag_key,
569
+ feature_enabled: feature_enabled,
570
+ source: source_string,
571
+ variable_values: all_variables,
572
+ source_info: source_info || {}
573
+ )
574
+
575
+ all_variables
576
+ end
577
+
487
578
  # Get the Integer value of the specified variable in the feature flag.
488
579
  #
489
580
  # @param feature_flag_key - String key of feature flag the variable belongs to
@@ -649,8 +740,6 @@ module Optimizely
649
740
  # Error message logged in DatafileProjectConfig- get_feature_flag_from_key
650
741
  return nil if variable.nil?
651
742
 
652
- feature_enabled = false
653
-
654
743
  # If variable_type is nil, set it equal to variable['type']
655
744
  variable_type ||= variable['type']
656
745
  # Returns nil if type differs
@@ -658,43 +747,24 @@ module Optimizely
658
747
  @logger.log(Logger::WARN,
659
748
  "Requested variable as type '#{variable_type}' but variable '#{variable_key}' is of type '#{variable['type']}'.")
660
749
  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
750
  end
695
751
 
752
+ decision = @decision_service.get_variation_for_feature(config, feature_flag, user_id, attributes)
753
+ variation = decision ? decision['variation'] : nil
754
+ feature_enabled = variation ? variation['featureEnabled'] : false
755
+
756
+ variable_value = get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
696
757
  variable_value = Helpers::VariableType.cast_value_to_type(variable_value, variable_type, @logger)
697
758
 
759
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']
760
+ if decision && decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
761
+ source_info = {
762
+ experiment_key: decision.experiment['key'],
763
+ variation_key: variation['key']
764
+ }
765
+ source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST']
766
+ end
767
+
698
768
  @notification_center.send_notifications(
699
769
  NotificationCenter::NOTIFICATION_TYPES[:DECISION],
700
770
  Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_VARIABLE'], user_id, (attributes || {}),
@@ -710,6 +780,45 @@ module Optimizely
710
780
  variable_value
711
781
  end
712
782
 
783
+ def get_feature_variable_for_variation(feature_flag_key, feature_enabled, variation, variable, user_id)
784
+ # Helper method to get the non type-casted value for a variable attached to a
785
+ # feature flag. Returns appropriate variable value depending on whether there
786
+ # was a matching variation, feature was enabled or not or varible was part of the
787
+ # available variation or not. Also logs the appropriate message explaining how it
788
+ # evaluated the value of the variable.
789
+ #
790
+ # feature_flag_key - String key of feature flag the variable belongs to
791
+ # feature_enabled - Boolean indicating if feature is enabled or not
792
+ # variation - varition returned by decision service
793
+ # user_id - String user ID
794
+ #
795
+ # Returns string value of the variable.
796
+
797
+ config = project_config
798
+ variable_value = variable['defaultValue']
799
+ if variation
800
+ if feature_enabled == true
801
+ variation_variable_usages = config.variation_id_to_variable_usage_map[variation['id']]
802
+ variable_id = variable['id']
803
+ if variation_variable_usages&.key?(variable_id)
804
+ variable_value = variation_variable_usages[variable_id]['value']
805
+ @logger.log(Logger::INFO,
806
+ "Got variable value '#{variable_value}' for variable '#{variable['key']}' of feature flag '#{feature_flag_key}'.")
807
+ else
808
+ @logger.log(Logger::DEBUG,
809
+ "Variable '#{variable['key']}' is not used in variation '#{variation['key']}'. Returning the default variable value '#{variable_value}'.")
810
+ end
811
+ else
812
+ @logger.log(Logger::DEBUG,
813
+ "Feature '#{feature_flag_key}' for variation '#{variation['key']}' is not enabled. Returning the default variable value '#{variable_value}'.")
814
+ end
815
+ else
816
+ @logger.log(Logger::INFO,
817
+ "User '#{user_id}' was not bucketed into any variation for feature flag '#{feature_flag_key}'. Returning the default variable value '#{variable_value}'.")
818
+ end
819
+ variable_value
820
+ end
821
+
713
822
  def user_inputs_valid?(attributes = nil, event_tags = nil)
714
823
  # Helper method to validate user inputs.
715
824
  #
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2017, 2019, Optimizely and contributors
4
+ # Copyright 2016-2017, 2019-2020, 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.
@@ -84,7 +84,7 @@ module Optimizely
84
84
  result = ConditionTreeEvaluator.evaluate(audience_conditions, evaluate_custom_attr)
85
85
  result_str = result.nil? ? 'UNKNOWN' : result.to_s.upcase
86
86
  logger.log(
87
- Logger::INFO,
87
+ Logger::DEBUG,
88
88
  format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['AUDIENCE_EVALUATION_RESULT'], audience_id, result_str)
89
89
  )
90
90
  result
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2019, Optimizely and contributors
3
+ # Copyright 2019-2020, Optimizely and contributors
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
@@ -82,6 +82,17 @@ module Optimizely
82
82
  @revision = config['revision']
83
83
  @rollouts = config.fetch('rollouts', [])
84
84
 
85
+ # Json type is represented in datafile as a subtype of string for the sake of backwards compatibility.
86
+ # Converting it to a first-class json type while creating Project Config
87
+ @feature_flags.each do |feature_flag|
88
+ feature_flag['variables'].each do |variable|
89
+ if variable['type'] == 'string' && variable['subType'] == 'json'
90
+ variable['type'] = 'json'
91
+ variable.delete('subType')
92
+ end
93
+ end
94
+ end
95
+
85
96
  # Utility maps for quick lookup
86
97
  @attribute_key_map = generate_key_map(@attributes, 'key')
87
98
  @event_key_map = generate_key_map(@events, 'key')
@@ -159,8 +170,9 @@ module Optimizely
159
170
  config = new(datafile, logger, error_handler)
160
171
  rescue StandardError => e
161
172
  default_logger = SimpleLogger.new
162
- error_msg = e.class == InvalidDatafileVersionError ? e.message : InvalidInputError.new('datafile').message
163
- error_to_handle = e.class == InvalidDatafileVersionError ? InvalidDatafileVersionError : InvalidInputError
173
+ error_to_handle = e.class == InvalidDatafileVersionError ? e : InvalidInputError.new('datafile')
174
+ error_msg = error_to_handle.message
175
+
164
176
  default_logger.log(Logger::ERROR, error_msg)
165
177
  error_handler.handle_error error_to_handle
166
178
  return nil
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2019, Optimizely and contributors
4
+ # Copyright 2019-2020, 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.
@@ -19,13 +19,14 @@ module Optimizely
19
19
  class AsyncScheduler
20
20
  attr_reader :running
21
21
 
22
- def initialize(callback, interval, auto_update, logger = nil)
22
+ def initialize(callback, interval, auto_update, logger = nil, error_handler = nil)
23
23
  # Sets up AsyncScheduler to execute a callback periodically.
24
24
  #
25
25
  # callback - Main function to be executed periodically.
26
26
  # interval - How many seconds to wait between executions.
27
27
  # auto_update - boolean indicates to run infinitely or only once.
28
28
  # logger - Optional Provides a logger instance.
29
+ # error_handler - Optional Provides a handle_error method to handle exceptions.
29
30
 
30
31
  @callback = callback
31
32
  @interval = interval
@@ -33,6 +34,7 @@ module Optimizely
33
34
  @running = false
34
35
  @thread = nil
35
36
  @logger = logger || NoOpLogger.new
37
+ @error_handler = error_handler || NoOpErrorHandler.new
36
38
  end
37
39
 
38
40
  def start!
@@ -54,6 +56,7 @@ module Optimizely
54
56
  Logger::ERROR,
55
57
  "Couldn't create a new thread for async scheduler. #{e.message}"
56
58
  )
59
+ @error_handler.handle_error(e)
57
60
  end
58
61
  end
59
62
 
@@ -80,6 +83,7 @@ module Optimizely
80
83
  Logger::ERROR,
81
84
  "Something went wrong when executing passed callback. #{e.message}"
82
85
  )
86
+ @error_handler.handle_error(e)
83
87
  stop!
84
88
  end
85
89
  break unless @auto_update
@@ -19,14 +19,16 @@ require_relative '../config/datafile_project_config'
19
19
  require_relative '../error_handler'
20
20
  require_relative '../exceptions'
21
21
  require_relative '../helpers/constants'
22
+ require_relative '../helpers/http_utils'
22
23
  require_relative '../logger'
23
24
  require_relative '../notification_center'
24
25
  require_relative '../project_config'
25
26
  require_relative '../optimizely_config'
26
27
  require_relative 'project_config_manager'
27
28
  require_relative 'async_scheduler'
28
- require 'httparty'
29
+
29
30
  require 'json'
31
+
30
32
  module Optimizely
31
33
  class HTTPProjectConfigManager < ProjectConfigManager
32
34
  # Config manager that polls for the datafile and updated ProjectConfig based on an update interval.
@@ -49,6 +51,7 @@ module Optimizely
49
51
  # error_handler - Provides a handle_error method to handle exceptions.
50
52
  # skip_json_validation - Optional boolean param which allows skipping JSON schema
51
53
  # validation upon object invocation. By default JSON schema validation will be performed.
54
+ # datafile_access_token - access token used to fetch private datafiles
52
55
  def initialize(
53
56
  sdk_key: nil,
54
57
  url: nil,
@@ -61,10 +64,12 @@ module Optimizely
61
64
  logger: nil,
62
65
  error_handler: nil,
63
66
  skip_json_validation: false,
64
- notification_center: nil
67
+ notification_center: nil,
68
+ datafile_access_token: nil
65
69
  )
66
70
  @logger = logger || NoOpLogger.new
67
71
  @error_handler = error_handler || NoOpErrorHandler.new
72
+ @access_token = datafile_access_token
68
73
  @datafile_url = get_datafile_url(sdk_key, url, url_template)
69
74
  @polling_interval = nil
70
75
  polling_interval(polling_interval)
@@ -148,16 +153,13 @@ module Optimizely
148
153
  "Fetching datafile from #{@datafile_url}"
149
154
  )
150
155
  begin
151
- headers = {
152
- 'Content-Type' => 'application/json'
153
- }
154
-
155
- headers[Helpers::Constants::HTTP_HEADERS['LAST_MODIFIED']] = @last_modified if @last_modified
156
+ headers = {}
157
+ headers['Content-Type'] = 'application/json'
158
+ headers['If-Modified-Since'] = @last_modified if @last_modified
159
+ headers['Authorization'] = "Bearer #{@access_token}" unless @access_token.nil?
156
160
 
157
- response = HTTParty.get(
158
- @datafile_url,
159
- headers: headers,
160
- timeout: Helpers::Constants::CONFIG_MANAGER['REQUEST_TIMEOUT']
161
+ response = Helpers::HttpUtils.make_request(
162
+ @datafile_url, :get, nil, headers, Helpers::Constants::CONFIG_MANAGER['REQUEST_TIMEOUT']
161
163
  )
162
164
  rescue StandardError => e
163
165
  @logger.log(
@@ -284,17 +286,19 @@ module Optimizely
284
286
  # SDK key to determine URL from which to fetch the datafile.
285
287
  # Returns String representing URL to fetch datafile from.
286
288
  if sdk_key.nil? && url.nil?
287
- @logger.log(Logger::ERROR, 'Must provide at least one of sdk_key or url.')
288
- @error_handler.handle_error(InvalidInputsError)
289
+ error_msg = 'Must provide at least one of sdk_key or url.'
290
+ @logger.log(Logger::ERROR, error_msg)
291
+ @error_handler.handle_error(InvalidInputsError.new(error_msg))
289
292
  end
290
293
 
291
294
  unless url
292
- url_template ||= Helpers::Constants::CONFIG_MANAGER['DATAFILE_URL_TEMPLATE']
295
+ url_template ||= @access_token.nil? ? Helpers::Constants::CONFIG_MANAGER['DATAFILE_URL_TEMPLATE'] : Helpers::Constants::CONFIG_MANAGER['AUTHENTICATED_DATAFILE_URL_TEMPLATE']
293
296
  begin
294
297
  return (url_template % sdk_key)
295
298
  rescue
296
- @logger.log(Logger::ERROR, "Invalid url_template #{url_template} provided.")
297
- @error_handler.handle_error(InvalidInputsError)
299
+ error_msg = "Invalid url_template #{url_template} provided."
300
+ @logger.log(Logger::ERROR, error_msg)
301
+ @error_handler.handle_error(InvalidInputsError.new(error_msg))
298
302
  end
299
303
  end
300
304
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2017, 2019, Optimizely and contributors
4
+ # Copyright 2016-2017, 2019-2020 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.
@@ -16,8 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
  require_relative 'exceptions'
19
-
20
- require 'httparty'
19
+ require_relative 'helpers/http_utils'
21
20
 
22
21
  module Optimizely
23
22
  class NoOpEventDispatcher
@@ -39,19 +38,13 @@ module Optimizely
39
38
  #
40
39
  # @param event - Event object
41
40
  def dispatch_event(event)
42
- if event.http_verb == :get
43
- response = HTTParty.get(event.url, headers: event.headers, query: event.params, timeout: REQUEST_TIMEOUT)
44
-
45
- elsif event.http_verb == :post
46
- response = HTTParty.post(event.url,
47
- body: event.params.to_json,
48
- headers: event.headers,
49
- timeout: REQUEST_TIMEOUT)
50
- end
41
+ response = Helpers::HttpUtils.make_request(
42
+ event.url, event.http_verb, event.params.to_json, event.headers, REQUEST_TIMEOUT
43
+ )
51
44
 
52
45
  error_msg = "Event failed to dispatch with response code: #{response.code}"
53
46
 
54
- case response.code
47
+ case response.code.to_i
55
48
  when 400...500
56
49
  @logger.log(Logger::ERROR, error_msg)
57
50
  @error_handler.handle_error(HTTPCallError.new("HTTP Client Error: #{response.code}"))
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2019, Optimizely and contributors
4
+ # Copyright 2016-2020, 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.
@@ -81,14 +81,6 @@ module Optimizely
81
81
  end
82
82
  end
83
83
 
84
- class InvalidDatafileError < Error
85
- # Raised when a public method fails due to an invalid datafile
86
-
87
- def initialize(aborted_method)
88
- super("Provided datafile is in an invalid format. Aborting #{aborted_method}.")
89
- end
90
- end
91
-
92
84
  class InvalidDatafileVersionError < Error
93
85
  # Raised when a datafile with an unsupported version is provided
94
86
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2019, Optimizely and contributors
4
+ # Copyright 2016-2020, 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.
@@ -304,7 +304,8 @@ module Optimizely
304
304
  'BOOLEAN' => 'boolean',
305
305
  'DOUBLE' => 'double',
306
306
  'INTEGER' => 'integer',
307
- 'STRING' => 'string'
307
+ 'STRING' => 'string',
308
+ 'JSON' => 'json'
308
309
  }.freeze
309
310
 
310
311
  INPUT_VARIABLES = {
@@ -357,11 +358,13 @@ module Optimizely
357
358
  'AB_TEST' => 'ab-test',
358
359
  'FEATURE' => 'feature',
359
360
  'FEATURE_TEST' => 'feature-test',
360
- 'FEATURE_VARIABLE' => 'feature-variable'
361
+ 'FEATURE_VARIABLE' => 'feature-variable',
362
+ 'ALL_FEATURE_VARIABLES' => 'all-feature-variables'
361
363
  }.freeze
362
364
 
363
365
  CONFIG_MANAGER = {
364
366
  'DATAFILE_URL_TEMPLATE' => 'https://cdn.optimizely.com/datafiles/%s.json',
367
+ 'AUTHENTICATED_DATAFILE_URL_TEMPLATE' => 'https://config.optimizely.com/datafiles/auth/%s.json',
365
368
  # Default time in seconds to block the 'config' method call until 'config' instance has been initialized.
366
369
  'DEFAULT_BLOCKING_TIMEOUT' => 15,
367
370
  # Default config update interval of 5 minutes
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2020, Optimizely and contributors
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'net/http'
20
+
21
+ module Optimizely
22
+ module Helpers
23
+ module HttpUtils
24
+ module_function
25
+
26
+ def make_request(url, http_method, request_body = nil, headers = {}, read_timeout = nil)
27
+ # makes http/https GET/POST request and returns response
28
+
29
+ uri = URI.parse(url)
30
+ http = Net::HTTP.new(uri.host, uri.port)
31
+
32
+ http.read_timeout = read_timeout if read_timeout
33
+ http.use_ssl = uri.scheme == 'https'
34
+
35
+ if http_method == :get
36
+ request = Net::HTTP::Get.new(uri.request_uri)
37
+ elsif http_method == :post
38
+ request = Net::HTTP::Post.new(uri.request_uri)
39
+ request.body = request_body if request_body
40
+ else
41
+ return nil
42
+ end
43
+
44
+ # set headers
45
+ headers&.each do |key, val|
46
+ request[key] = val
47
+ end
48
+
49
+ http.request(request)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2017, Optimizely and contributors
4
+ # Copyright 2017, 2020, 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.
@@ -48,6 +48,13 @@ module Optimizely
48
48
  logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type "\
49
49
  "'#{variable_type}': #{e.message}.")
50
50
  end
51
+ when 'json'
52
+ begin
53
+ return_value = JSON.parse(value)
54
+ rescue => e
55
+ logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type "\
56
+ "'#{variable_type}': #{e.message}.")
57
+ end
51
58
  else
52
59
  # default case is string
53
60
  return_value = value
@@ -17,16 +17,18 @@
17
17
  #
18
18
 
19
19
  require 'optimizely'
20
+ require 'optimizely/error_handler'
20
21
  require 'optimizely/event_dispatcher'
21
22
  require 'optimizely/event/batch_event_processor'
23
+ require 'optimizely/logger'
24
+ require 'optimizely/notification_center'
25
+
22
26
  module Optimizely
23
27
  class OptimizelyFactory
24
- attr_reader :max_event_batch_size, :max_event_flush_interval
25
-
26
28
  # Convenience method for setting the maximum number of events contained within a batch.
27
29
  # @param batch_size Integer - Sets size of EventQueue.
28
30
  # @param logger - Optional LoggerInterface Provides a log method to log messages.
29
- def self.max_event_batch_size(batch_size, logger)
31
+ def self.max_event_batch_size(batch_size, logger = NoOpLogger.new)
30
32
  unless batch_size.is_a? Integer
31
33
  logger.log(
32
34
  Logger::ERROR,
@@ -48,7 +50,7 @@ module Optimizely
48
50
  # Convenience method for setting the maximum time interval in milliseconds between event dispatches.
49
51
  # @param flush_interval Numeric - Time interval between event dispatches.
50
52
  # @param logger - Optional LoggerInterface Provides a log method to log messages.
51
- def self.max_event_flush_interval(flush_interval, logger)
53
+ def self.max_event_flush_interval(flush_interval, logger = NoOpLogger.new)
52
54
  unless flush_interval.is_a? Numeric
53
55
  logger.log(
54
56
  Logger::ERROR,
@@ -67,12 +69,42 @@ module Optimizely
67
69
  @max_event_flush_interval = flush_interval
68
70
  end
69
71
 
72
+ # Convenience method for setting frequency at which datafile has to be polled and ProjectConfig updated.
73
+ #
74
+ # @param polling_interval Numeric - Time in seconds after which to update datafile.
75
+ def self.polling_interval(polling_interval)
76
+ @polling_interval = polling_interval
77
+ end
78
+
79
+ # Convenience method for setting timeout to block the config call until config has been initialized.
80
+ #
81
+ # @param blocking_timeout Numeric - Time in seconds.
82
+ def self.blocking_timeout(blocking_timeout)
83
+ @blocking_timeout = blocking_timeout
84
+ end
85
+
70
86
  # Returns a new optimizely instance.
71
87
  #
72
88
  # @params sdk_key - Required String uniquely identifying the fallback datafile corresponding to project.
73
89
  # @param fallback datafile - Optional JSON string datafile.
74
90
  def self.default_instance(sdk_key, datafile = nil)
75
- Optimizely::Project.new(datafile, nil, nil, nil, nil, nil, sdk_key)
91
+ error_handler = NoOpErrorHandler.new
92
+ logger = NoOpLogger.new
93
+ notification_center = NotificationCenter.new(logger, error_handler)
94
+
95
+ config_manager = Optimizely::HTTPProjectConfigManager.new(
96
+ sdk_key: sdk_key,
97
+ polling_interval: @polling_interval,
98
+ blocking_timeout: @blocking_timeout,
99
+ datafile: datafile,
100
+ logger: logger,
101
+ error_handler: error_handler,
102
+ notification_center: notification_center
103
+ )
104
+
105
+ Optimizely::Project.new(
106
+ datafile, nil, logger, error_handler, nil, nil, sdk_key, config_manager, notification_center
107
+ )
76
108
  end
77
109
 
78
110
  # Returns a new optimizely instance.
@@ -108,10 +140,27 @@ module Optimizely
108
140
  config_manager = nil,
109
141
  notification_center = nil
110
142
  )
143
+
144
+ error_handler ||= NoOpErrorHandler.new
145
+ logger ||= NoOpLogger.new
146
+ notification_center = notification_center.is_a?(Optimizely::NotificationCenter) ? notification_center : NotificationCenter.new(logger, error_handler)
147
+
111
148
  event_processor = BatchEventProcessor.new(
112
149
  event_dispatcher: event_dispatcher || EventDispatcher.new,
113
150
  batch_size: @max_event_batch_size,
114
151
  flush_interval: @max_event_flush_interval,
152
+ logger: logger,
153
+ notification_center: notification_center
154
+ )
155
+
156
+ config_manager ||= Optimizely::HTTPProjectConfigManager.new(
157
+ sdk_key: sdk_key,
158
+ polling_interval: @polling_interval,
159
+ blocking_timeout: @blocking_timeout,
160
+ datafile: datafile,
161
+ logger: logger,
162
+ error_handler: error_handler,
163
+ skip_json_validation: skip_json_validation,
115
164
  notification_center: notification_center
116
165
  )
117
166
 
@@ -17,5 +17,5 @@
17
17
  #
18
18
  module Optimizely
19
19
  CLIENT_ENGINE = 'ruby-sdk'
20
- VERSION = '3.4.0'
20
+ VERSION = '3.5.0-beta'
21
21
  end
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: 3.4.0
4
+ version: 3.5.0.pre.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Optimizely
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-23 00:00:00.000000000 Z
11
+ date: 2020-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,20 +94,6 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: httparty
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '0.11'
104
- type: :runtime
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '0.11'
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: json-schema
113
99
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +164,7 @@ files:
178
164
  - lib/optimizely/helpers/date_time_utils.rb
179
165
  - lib/optimizely/helpers/event_tag_utils.rb
180
166
  - lib/optimizely/helpers/group.rb
167
+ - lib/optimizely/helpers/http_utils.rb
181
168
  - lib/optimizely/helpers/validator.rb
182
169
  - lib/optimizely/helpers/variable_type.rb
183
170
  - lib/optimizely/logger.rb
@@ -203,9 +190,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
203
190
  version: '0'
204
191
  required_rubygems_version: !ruby/object:Gem::Requirement
205
192
  requirements:
206
- - - ">="
193
+ - - ">"
207
194
  - !ruby/object:Gem::Version
208
- version: '0'
195
+ version: 1.3.1
209
196
  requirements: []
210
197
  rubygems_version: 3.0.3
211
198
  signing_key: