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.
- checksums.yaml +4 -4
- data/lib/optimizely/audience.rb +5 -5
- data/lib/optimizely/config/datafile_project_config.rb +542 -539
- data/lib/optimizely/config_manager/http_project_config_manager.rb +12 -4
- data/lib/optimizely/config_manager/project_config_manager.rb +2 -1
- data/lib/optimizely/config_manager/static_project_config_manager.rb +3 -2
- data/lib/optimizely/event_dispatcher.rb +2 -4
- data/lib/optimizely/exceptions.rb +15 -1
- data/lib/optimizely/helpers/constants.rb +45 -1
- data/lib/optimizely/helpers/http_utils.rb +3 -0
- data/lib/optimizely/helpers/sdk_settings.rb +61 -0
- data/lib/optimizely/helpers/validator.rb +55 -0
- data/lib/optimizely/notification_center_registry.rb +71 -0
- data/lib/optimizely/odp/lru_cache.rb +114 -0
- data/lib/optimizely/odp/odp_config.rb +102 -0
- data/lib/optimizely/odp/odp_event.rb +75 -0
- data/lib/optimizely/odp/odp_event_api_manager.rb +70 -0
- data/lib/optimizely/odp/odp_event_manager.rb +286 -0
- data/lib/optimizely/odp/odp_manager.rb +159 -0
- data/lib/optimizely/odp/odp_segment_api_manager.rb +122 -0
- data/lib/optimizely/odp/odp_segment_manager.rb +97 -0
- data/lib/optimizely/optimizely_config.rb +1 -1
- data/lib/optimizely/optimizely_factory.rb +7 -2
- data/lib/optimizely/optimizely_user_context.rb +40 -6
- data/lib/optimizely/user_condition_evaluator.rb +1 -1
- data/lib/optimizely/version.rb +2 -2
- data/lib/optimizely.rb +142 -10
- metadata +14 -4
@@ -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
|
41
|
-
# datafile
|
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
|