optimizely-sdk 4.0.1 → 5.0.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.
@@ -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)
@@ -222,6 +226,10 @@ module Optimizely
222
226
 
223
227
  @notification_center.send_notifications(NotificationCenter::NOTIFICATION_TYPES[:OPTIMIZELY_CONFIG_UPDATE])
224
228
 
229
+ NotificationCenterRegistry
230
+ .get_notification_center(@sdk_key, @logger)
231
+ &.send_notifications(NotificationCenter::NOTIFICATION_TYPES[:OPTIMIZELY_CONFIG_UPDATE])
232
+
225
233
  @logger.log(Logger::DEBUG, 'Received new datafile and updated config. ' \
226
234
  "Old revision number: #{previous_revision}. New revision number: #{@config.revision}.")
227
235
  end
@@ -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,6 +41,7 @@ module Optimizely
41
41
  error_handler,
42
42
  skip_json_validation
43
43
  )
44
+ @sdk_key = @config&.sdk_key
44
45
  @optimizely_config = nil
45
46
  end
46
47
 
@@ -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,6 +25,20 @@ 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
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2020, Optimizely and contributors
4
+ # Copyright 2016-2020, 2022, 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.
@@ -382,6 +382,15 @@ module Optimizely
382
382
  'EVALUATING_AUDIENCES_COMBINED' => "Evaluating audiences for rule '%s': %s."
383
383
  }.merge(AUDIENCE_EVALUATION_LOGS).freeze
384
384
 
385
+ ODP_LOGS = {
386
+ FETCH_SEGMENTS_FAILED: 'Audience segments fetch failed (%s).',
387
+ ODP_EVENT_FAILED: 'ODP event send failed (%s).',
388
+ ODP_NOT_ENABLED: 'ODP is not enabled.',
389
+ ODP_NOT_INTEGRATED: 'ODP is not integrated.',
390
+ ODP_INVALID_DATA: 'ODP data is not valid.',
391
+ ODP_INVALID_ACTION: 'ODP action is not valid (cannot be empty).'
392
+ }.freeze
393
+
385
394
  DECISION_NOTIFICATION_TYPES = {
386
395
  'AB_TEST' => 'ab-test',
387
396
  'FEATURE' => 'feature',
@@ -406,6 +415,41 @@ module Optimizely
406
415
  'REQUEST_TIMEOUT' => 10
407
416
  }.freeze
408
417
 
418
+ EVENT_DISPATCH_CONFIG = {
419
+ REQUEST_TIMEOUT: 10
420
+ }.freeze
421
+
422
+ ODP_GRAPHQL_API_CONFIG = {
423
+ REQUEST_TIMEOUT: 10
424
+ }.freeze
425
+
426
+ ODP_REST_API_CONFIG = {
427
+ REQUEST_TIMEOUT: 10
428
+ }.freeze
429
+
430
+ ODP_SEGMENTS_CACHE_CONFIG = {
431
+ DEFAULT_CAPACITY: 10_000,
432
+ DEFAULT_TIMEOUT_SECONDS: 600
433
+ }.freeze
434
+
435
+ ODP_MANAGER_CONFIG = {
436
+ KEY_FOR_USER_ID: 'fs_user_id',
437
+ EVENT_TYPE: 'fullstack'
438
+ }.freeze
439
+
440
+ ODP_CONFIG_STATE = {
441
+ UNDETERMINED: 'UNDETERMINED',
442
+ INTEGRATED: 'INTEGRATED',
443
+ NOT_INTEGRATED: 'NOT_INTEGRATED'
444
+ }.freeze
445
+
446
+ ODP_EVENT_MANAGER = {
447
+ DEFAULT_QUEUE_CAPACITY: 10_000,
448
+ DEFAULT_BATCH_SIZE: 10,
449
+ DEFAULT_FLUSH_INTERVAL_SECONDS: 1,
450
+ DEFAULT_RETRY_COUNT: 3
451
+ }.freeze
452
+
409
453
  HTTP_HEADERS = {
410
454
  'IF_MODIFIED_SINCE' => 'If-Modified-Since',
411
455
  'LAST_MODIFIED' => 'Last-Modified'
@@ -17,6 +17,7 @@
17
17
  #
18
18
 
19
19
  require 'net/http'
20
+ require_relative '../exceptions'
20
21
 
21
22
  module Optimizely
22
23
  module Helpers
@@ -28,6 +29,8 @@ module Optimizely
28
29
  #
29
30
  uri = URI.parse(url)
30
31
 
32
+ raise HTTPUriError unless uri.respond_to?(:request_uri)
33
+
31
34
  case http_method
32
35
  when :get
33
36
  request = Net::HTTP::Get.new(uri.request_uri)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2022, 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_relative 'constants'
20
+
21
+ module Optimizely
22
+ module Helpers
23
+ class OptimizelySdkSettings
24
+ attr_accessor :odp_disabled, :segments_cache_size, :segments_cache_timeout_in_secs, :odp_segments_cache, :odp_segment_manager,
25
+ :odp_event_manager, :fetch_segments_timeout, :odp_event_timeout, :odp_flush_interval
26
+
27
+ # Contains configuration used for Optimizely Project initialization.
28
+ #
29
+ # @param disable_odp - Set this flag to true (default = false) to disable ODP features.
30
+ # @param segments_cache_size - The maximum size of audience segments cache (optional. default = 10,000). Set to zero to disable caching.
31
+ # @param segments_cache_timeout_in_secs - The timeout in seconds of audience segments cache (optional. default = 600). Set to zero to disable timeout.
32
+ # @param odp_segments_cache - A custom odp segments cache. Required methods include: `save(key, value)`, `lookup(key) -> value`, and `reset()`
33
+ # @param odp_segment_manager - A custom odp segment manager. Required method is: `fetch_qualified_segments(user_key, user_value, options)`.
34
+ # @param odp_event_manager - A custom odp event manager. Required method is: `send_event(type:, action:, identifiers:, data:)`
35
+ # @param odp_segment_request_timeout - Time to wait in seconds for fetch_qualified_segments (optional. default = 10).
36
+ # @param odp_event_request_timeout - Time to wait in seconds for send_odp_events (optional. default = 10).
37
+ # @param odp_event_flush_interval - Time to wait in seconds for odp events to accumulate before sending (optional. default = 1).
38
+ def initialize(
39
+ disable_odp: false,
40
+ segments_cache_size: Constants::ODP_SEGMENTS_CACHE_CONFIG[:DEFAULT_CAPACITY],
41
+ segments_cache_timeout_in_secs: Constants::ODP_SEGMENTS_CACHE_CONFIG[:DEFAULT_TIMEOUT_SECONDS],
42
+ odp_segments_cache: nil,
43
+ odp_segment_manager: nil,
44
+ odp_event_manager: nil,
45
+ odp_segment_request_timeout: nil,
46
+ odp_event_request_timeout: nil,
47
+ odp_event_flush_interval: nil
48
+ )
49
+ @odp_disabled = disable_odp
50
+ @segments_cache_size = segments_cache_size
51
+ @segments_cache_timeout_in_secs = segments_cache_timeout_in_secs
52
+ @odp_segments_cache = odp_segments_cache
53
+ @odp_segment_manager = odp_segment_manager
54
+ @odp_event_manager = odp_event_manager
55
+ @fetch_segments_timeout = odp_segment_request_timeout
56
+ @odp_event_timeout = odp_event_request_timeout
57
+ @odp_flush_interval = odp_event_flush_interval
58
+ end
59
+ end
60
+ end
61
+ end
@@ -178,6 +178,61 @@ module Optimizely
178
178
 
179
179
  value.is_a?(Numeric) && value.to_f.finite? && value.abs <= Constants::FINITE_NUMBER_LIMIT
180
180
  end
181
+
182
+ def odp_data_types_valid?(data)
183
+ valid_types = [String, Float, Integer, TrueClass, FalseClass, NilClass]
184
+ data&.values&.all? { |e| valid_types.member? e.class }
185
+ end
186
+
187
+ def segments_cache_valid?(segments_cache)
188
+ # Determines if a given segments_cache is valid.
189
+ #
190
+ # segments_cache - custom cache to be validated.
191
+ #
192
+ # Returns boolean depending on whether cache has required methods.
193
+ (
194
+ segments_cache.respond_to?(:reset) &&
195
+ segments_cache.method(:reset)&.parameters&.empty? &&
196
+ segments_cache.respond_to?(:lookup) &&
197
+ segments_cache.method(:lookup)&.parameters&.length&.positive? &&
198
+ segments_cache.respond_to?(:save) &&
199
+ segments_cache.method(:save)&.parameters&.length&.positive?
200
+ )
201
+ end
202
+
203
+ def segment_manager_valid?(segment_manager)
204
+ # Determines if a given segment_manager is valid.
205
+ #
206
+ # segment_manager - custom manager to be validated.
207
+ #
208
+ # Returns boolean depending on whether manager has required methods.
209
+ (
210
+ segment_manager.respond_to?(:odp_config) &&
211
+ segment_manager.respond_to?(:reset) &&
212
+ segment_manager.method(:reset)&.parameters&.empty? &&
213
+ segment_manager.respond_to?(:fetch_qualified_segments) &&
214
+ (segment_manager.method(:fetch_qualified_segments)&.parameters&.length || 0) >= 3
215
+ )
216
+ end
217
+
218
+ def event_manager_valid?(event_manager)
219
+ # Determines if a given event_manager is valid.
220
+ #
221
+ # event_manager - custom manager to be validated.
222
+ #
223
+ # Returns boolean depending on whether manager has required method and parameters.
224
+ return false unless
225
+ event_manager.respond_to?(:send_event) &&
226
+ event_manager.respond_to?(:start!) &&
227
+ (event_manager.method(:start!)&.parameters&.length || 0) >= 1 &&
228
+ event_manager.respond_to?(:update_config) &&
229
+ event_manager.respond_to?(:stop!)
230
+
231
+ required_parameters = Set[%i[keyreq type], %i[keyreq action], %i[keyreq identifiers], %i[keyreq data]]
232
+ existing_parameters = event_manager.method(:send_event).parameters.to_set
233
+
234
+ existing_parameters & required_parameters == required_parameters
235
+ end
181
236
  end
182
237
  end
183
238
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2023, 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
+ require_relative 'notification_center'
19
+ require_relative 'exceptions'
20
+
21
+ module Optimizely
22
+ class NotificationCenterRegistry
23
+ private_class_method :new
24
+ # Class managing internal notification centers.
25
+ # @api no-doc
26
+ @notification_centers = {}
27
+ @mutex = Mutex.new
28
+
29
+ # Returns an internal notification center for the given sdk_key, creating one
30
+ # if none exists yet.
31
+ #
32
+ # Args:
33
+ # sdk_key: A string sdk key to uniquely identify the notification center.
34
+ # logger: Optional logger.
35
+
36
+ # Returns:
37
+ # nil or NotificationCenter
38
+ def self.get_notification_center(sdk_key, logger)
39
+ unless sdk_key
40
+ logger&.log(Logger::ERROR, "#{MissingSdkKeyError.new.message} ODP may not work properly without it.")
41
+ return nil
42
+ end
43
+
44
+ notification_center = nil
45
+
46
+ @mutex.synchronize do
47
+ if @notification_centers.key?(sdk_key)
48
+ notification_center = @notification_centers[sdk_key]
49
+ else
50
+ notification_center = NotificationCenter.new(logger, nil)
51
+ @notification_centers[sdk_key] = notification_center
52
+ end
53
+ end
54
+
55
+ notification_center
56
+ end
57
+
58
+ # Remove a previously added notification center and clear all its listeners.
59
+
60
+ # Args:
61
+ # sdk_key: The sdk_key of the notification center to remove.
62
+ def self.remove_notification_center(sdk_key)
63
+ @mutex.synchronize do
64
+ @notification_centers
65
+ .delete(sdk_key)
66
+ &.clear_all_notification_listeners
67
+ end
68
+ nil
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2022, 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
+ module Optimizely
20
+ class LRUCache
21
+ # Least Recently Used cache that invalidates entries older than the timeout.
22
+
23
+ attr_reader :capacity, :timeout
24
+
25
+ def initialize(capacity, timeout_in_secs)
26
+ # @param capacity - The max size of the cache. If set <= 0, caching is disabled.
27
+ # @param timeout_in_secs - Seconds until a cache item is considered stale.
28
+ # If set <= 0, items never expire.
29
+ @cache_mutex = Mutex.new
30
+ @map = {}
31
+ @capacity = capacity
32
+ @timeout = timeout_in_secs
33
+ end
34
+
35
+ # Retrieve the non stale value from the cache corresponding to the provided key
36
+ # or nil if key is not found
37
+ # Moves the key/value pair to the end of the cache
38
+ #
39
+ # @param key - The key to retrieve
40
+
41
+ def lookup(key)
42
+ return nil if @capacity <= 0
43
+
44
+ @cache_mutex.synchronize do
45
+ return nil unless @map.include?(key)
46
+
47
+ element = @map.delete(key)
48
+ return nil if element.stale?(@timeout)
49
+
50
+ @map[key] = element
51
+
52
+ element.value
53
+ end
54
+ end
55
+
56
+ # Save a key/value pair into the cache
57
+ # Moves the key/value pair to the end of the cache
58
+ #
59
+ # @param key - A user key
60
+ # @param value - A user value
61
+
62
+ def save(key, value)
63
+ return if @capacity <= 0
64
+
65
+ @cache_mutex.synchronize do
66
+ @map.delete(key) if @map.key?(key)
67
+
68
+ @map[key] = CacheElement.new(value)
69
+
70
+ @map.delete(@map.first[0]) if @map.size > @capacity
71
+ nil
72
+ end
73
+ end
74
+
75
+ # Clears the cache
76
+
77
+ def reset
78
+ return if @capacity <= 0
79
+
80
+ @cache_mutex.synchronize { @map.clear }
81
+ nil
82
+ end
83
+
84
+ # Retrieve a value from the cache for a given key or nil if key is not found
85
+ # Doesn't update the cache
86
+ #
87
+ # @param key - The key to retrieve
88
+
89
+ def peek(key)
90
+ return nil if @capacity <= 0
91
+
92
+ @cache_mutex.synchronize { @map[key]&.value }
93
+ end
94
+ end
95
+
96
+ class CacheElement
97
+ # Individual element for the LRUCache.
98
+ attr_reader :value, :timestamp
99
+
100
+ def initialize(value)
101
+ @value = value
102
+ @timestamp = Time.new
103
+ end
104
+
105
+ def stale?(timeout)
106
+ # Returns true if the provided timeout has passed since the element's timestamp.
107
+ #
108
+ # @param timeout - The duration to check against
109
+ return false if timeout <= 0
110
+
111
+ Time.new - @timestamp >= timeout
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2022, 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 'optimizely/logger'
20
+ require_relative '../helpers/constants'
21
+
22
+ module Optimizely
23
+ class OdpConfig
24
+ ODP_CONFIG_STATE = Helpers::Constants::ODP_CONFIG_STATE
25
+ # Contains configuration used for ODP integration.
26
+ #
27
+ # @param api_host - The host URL for the ODP audience segments API (optional).
28
+ # @param api_key - The public API key for the ODP account from which the audience segments will be fetched (optional).
29
+ # @param segments_to_check - An array of all ODP segments used in the current datafile (associated with api_host/api_key).
30
+ def initialize(api_key = nil, api_host = nil, segments_to_check = [])
31
+ @api_key = api_key
32
+ @api_host = api_host
33
+ @segments_to_check = segments_to_check
34
+ @mutex = Mutex.new
35
+ @odp_state = @api_host.nil? || @api_key.nil? ? ODP_CONFIG_STATE[:UNDETERMINED] : ODP_CONFIG_STATE[:INTEGRATED]
36
+ end
37
+
38
+ # Replaces the existing configuration
39
+ #
40
+ # @param api_host - The host URL for the ODP audience segments API (optional).
41
+ # @param api_key - The public API key for the ODP account from which the audience segments will be fetched (optional).
42
+ # @param segments_to_check - An array of all ODP segments used in the current datafile (associated with api_host/api_key).
43
+ #
44
+ # @return - True if the provided values were different than the existing values.
45
+
46
+ def update(api_key = nil, api_host = nil, segments_to_check = [])
47
+ updated = false
48
+ @mutex.synchronize do
49
+ @odp_state = api_host.nil? || api_key.nil? ? ODP_CONFIG_STATE[:NOT_INTEGRATED] : ODP_CONFIG_STATE[:INTEGRATED]
50
+
51
+ if @api_key != api_key || @api_host != api_host || @segments_to_check != segments_to_check
52
+ @api_key = api_key
53
+ @api_host = api_host
54
+ @segments_to_check = segments_to_check
55
+ updated = true
56
+ end
57
+ end
58
+
59
+ updated
60
+ end
61
+
62
+ # Returns the api host for odp connections
63
+ #
64
+ # @return - The api host.
65
+
66
+ def api_host
67
+ @mutex.synchronize { @api_host.clone }
68
+ end
69
+
70
+ # Returns the api key for odp connections
71
+ #
72
+ # @return - The api key.
73
+
74
+ def api_key
75
+ @mutex.synchronize { @api_key.clone }
76
+ end
77
+
78
+ # Returns An array of qualified segments for this user
79
+ #
80
+ # @return - An array of segments names.
81
+
82
+ def segments_to_check
83
+ @mutex.synchronize { @segments_to_check.clone }
84
+ end
85
+
86
+ # Replace qualified segments with provided segments
87
+ #
88
+ # @param segments - An array of segment names
89
+
90
+ def segments_to_check=(segments_to_check)
91
+ @mutex.synchronize { @segments_to_check = segments_to_check.clone }
92
+ end
93
+
94
+ # Returns the state of odp integration (UNDETERMINED, INTEGRATED, NOT_INTEGRATED)
95
+ #
96
+ # @return - string
97
+
98
+ def odp_state
99
+ @mutex.synchronize { @odp_state }
100
+ end
101
+ end
102
+ end