optimizely-sdk 3.4.0 → 3.5.0.pre.beta

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: 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: