optimizely-sdk 5.0.0 → 5.0.1

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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +202 -202
  3. data/lib/optimizely/audience.rb +127 -127
  4. data/lib/optimizely/bucketer.rb +156 -156
  5. data/lib/optimizely/condition_tree_evaluator.rb +123 -123
  6. data/lib/optimizely/config/datafile_project_config.rb +558 -558
  7. data/lib/optimizely/config/proxy_config.rb +34 -34
  8. data/lib/optimizely/config_manager/async_scheduler.rb +95 -95
  9. data/lib/optimizely/config_manager/http_project_config_manager.rb +340 -340
  10. data/lib/optimizely/config_manager/project_config_manager.rb +25 -25
  11. data/lib/optimizely/config_manager/static_project_config_manager.rb +55 -55
  12. data/lib/optimizely/decide/optimizely_decide_option.rb +28 -28
  13. data/lib/optimizely/decide/optimizely_decision.rb +60 -60
  14. data/lib/optimizely/decide/optimizely_decision_message.rb +26 -26
  15. data/lib/optimizely/decision_service.rb +563 -563
  16. data/lib/optimizely/error_handler.rb +39 -39
  17. data/lib/optimizely/event/batch_event_processor.rb +235 -235
  18. data/lib/optimizely/event/entity/conversion_event.rb +44 -44
  19. data/lib/optimizely/event/entity/decision.rb +38 -38
  20. data/lib/optimizely/event/entity/event_batch.rb +86 -86
  21. data/lib/optimizely/event/entity/event_context.rb +50 -50
  22. data/lib/optimizely/event/entity/impression_event.rb +48 -48
  23. data/lib/optimizely/event/entity/snapshot.rb +33 -33
  24. data/lib/optimizely/event/entity/snapshot_event.rb +48 -48
  25. data/lib/optimizely/event/entity/user_event.rb +22 -22
  26. data/lib/optimizely/event/entity/visitor.rb +36 -36
  27. data/lib/optimizely/event/entity/visitor_attribute.rb +38 -38
  28. data/lib/optimizely/event/event_factory.rb +156 -156
  29. data/lib/optimizely/event/event_processor.rb +25 -25
  30. data/lib/optimizely/event/forwarding_event_processor.rb +44 -44
  31. data/lib/optimizely/event/user_event_factory.rb +88 -88
  32. data/lib/optimizely/event_builder.rb +221 -221
  33. data/lib/optimizely/event_dispatcher.rb +69 -69
  34. data/lib/optimizely/exceptions.rb +193 -193
  35. data/lib/optimizely/helpers/constants.rb +459 -459
  36. data/lib/optimizely/helpers/date_time_utils.rb +30 -30
  37. data/lib/optimizely/helpers/event_tag_utils.rb +132 -132
  38. data/lib/optimizely/helpers/group.rb +31 -31
  39. data/lib/optimizely/helpers/http_utils.rb +68 -68
  40. data/lib/optimizely/helpers/sdk_settings.rb +61 -61
  41. data/lib/optimizely/helpers/validator.rb +236 -236
  42. data/lib/optimizely/helpers/variable_type.rb +67 -67
  43. data/lib/optimizely/logger.rb +46 -46
  44. data/lib/optimizely/notification_center.rb +174 -174
  45. data/lib/optimizely/notification_center_registry.rb +71 -71
  46. data/lib/optimizely/odp/lru_cache.rb +114 -114
  47. data/lib/optimizely/odp/odp_config.rb +102 -102
  48. data/lib/optimizely/odp/odp_event.rb +75 -75
  49. data/lib/optimizely/odp/odp_event_api_manager.rb +70 -70
  50. data/lib/optimizely/odp/odp_event_manager.rb +286 -286
  51. data/lib/optimizely/odp/odp_manager.rb +159 -159
  52. data/lib/optimizely/odp/odp_segment_api_manager.rb +122 -122
  53. data/lib/optimizely/odp/odp_segment_manager.rb +97 -97
  54. data/lib/optimizely/optimizely_config.rb +273 -273
  55. data/lib/optimizely/optimizely_factory.rb +184 -184
  56. data/lib/optimizely/optimizely_user_context.rb +238 -238
  57. data/lib/optimizely/params.rb +31 -31
  58. data/lib/optimizely/project_config.rb +99 -99
  59. data/lib/optimizely/semantic_version.rb +166 -166
  60. data/lib/optimizely/user_condition_evaluator.rb +391 -391
  61. data/lib/optimizely/user_profile_service.rb +35 -35
  62. data/lib/optimizely/version.rb +21 -21
  63. data/lib/optimizely.rb +1262 -1262
  64. metadata +7 -5
@@ -1,46 +1,46 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright 2016-2017, 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
- require 'logger'
19
-
20
- module Optimizely
21
- class BaseLogger
22
- # Class encapsulating logging functionality. Override with your own logger providing log method.
23
-
24
- def log(_level, _message); end
25
- end
26
-
27
- class NoOpLogger < BaseLogger
28
- # Class providing log method which logs nothing.
29
-
30
- def log(_level, _message); end
31
- end
32
-
33
- class SimpleLogger < BaseLogger
34
- # Simple wrapper around Logger.
35
-
36
- def initialize(min_level = Logger::INFO)
37
- super()
38
- @logger = Logger.new($stdout)
39
- @logger.level = min_level
40
- end
41
-
42
- def log(level, message)
43
- @logger.add(level, message)
44
- end
45
- end
46
- end
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2016-2017, 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
+ require 'logger'
19
+
20
+ module Optimizely
21
+ class BaseLogger
22
+ # Class encapsulating logging functionality. Override with your own logger providing log method.
23
+
24
+ def log(_level, _message); end
25
+ end
26
+
27
+ class NoOpLogger < BaseLogger
28
+ # Class providing log method which logs nothing.
29
+
30
+ def log(_level, _message); end
31
+ end
32
+
33
+ class SimpleLogger < BaseLogger
34
+ # Simple wrapper around Logger.
35
+
36
+ def initialize(min_level = Logger::INFO)
37
+ super()
38
+ @logger = Logger.new($stdout)
39
+ @logger.level = min_level
40
+ end
41
+
42
+ def log(level, message)
43
+ @logger.add(level, message)
44
+ end
45
+ end
46
+ end
@@ -1,174 +1,174 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright 2017-2019, 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
- module Optimizely
19
- class NotificationCenter
20
- # @api no-doc
21
- attr_reader :notifications, :notification_id
22
-
23
- NOTIFICATION_TYPES = {
24
- # DEPRECATED: ACTIVATE notification type is deprecated since relase 3.1.0.
25
- ACTIVATE: 'ACTIVATE: experiment, user_id, attributes, variation, event',
26
- DECISION: 'DECISION: type, user_id, attributes, decision_info',
27
- LOG_EVENT: 'LOG_EVENT: type, log_event',
28
- OPTIMIZELY_CONFIG_UPDATE: 'optimizely_config_update',
29
- TRACK: 'TRACK: event_key, user_id, attributes, event_tags, event'
30
- }.freeze
31
-
32
- def initialize(logger, error_handler)
33
- @notification_id = 1
34
- @notifications = {}
35
- NOTIFICATION_TYPES.each_value { |value| @notifications[value] = [] }
36
- @logger = logger
37
- @error_handler = error_handler
38
- end
39
-
40
- # Adds notification callback to the notification center
41
- #
42
- # @param notification_type - One of the constants in NOTIFICATION_TYPES
43
- # @param notification_callback [lambda, Method, Callable] (default: block) - Called when the event is sent
44
- # @yield Block to be used as callback if callback omitted.
45
- #
46
- # @return [notification ID] Used to remove the notification
47
-
48
- def add_notification_listener(notification_type, notification_callback = nil, &block)
49
- return nil unless notification_type_valid?(notification_type)
50
-
51
- if notification_callback && block_given?
52
- @logger.log Logger::ERROR, 'Callback and block are mutually exclusive.'
53
- return nil
54
- end
55
-
56
- notification_callback ||= block
57
-
58
- unless notification_callback
59
- @logger.log Logger::ERROR, 'Callback can not be empty.'
60
- return nil
61
- end
62
-
63
- unless notification_callback.respond_to? :call
64
- @logger.log Logger::ERROR, 'Invalid notification callback given.'
65
- return nil
66
- end
67
-
68
- @notifications[notification_type].each do |notification|
69
- return -1 if notification[:callback] == notification_callback
70
- end
71
- @notifications[notification_type].push(notification_id: @notification_id, callback: notification_callback)
72
- notification_id = @notification_id
73
- @notification_id += 1
74
- notification_id
75
- end
76
-
77
- # Removes previously added notification callback
78
- #
79
- # @param notification_id
80
- #
81
- # @return [Boolean] true if found and removed, false otherwise
82
-
83
- def remove_notification_listener(notification_id)
84
- unless notification_id
85
- @logger.log Logger::ERROR, 'Notification ID can not be empty.'
86
- return nil
87
- end
88
- @notifications.each_key do |key|
89
- @notifications[key].each do |notification|
90
- if notification_id == notification[:notification_id]
91
- @notifications[key].delete(notification_id: notification_id, callback: notification[:callback])
92
- return true
93
- end
94
- end
95
- end
96
- false
97
- end
98
-
99
- # @deprecated Use {#clear_notification_listeners} instead.
100
- def clear_notifications(notification_type)
101
- @logger.log Logger::WARN, "'clear_notifications' is deprecated. Call 'clear_notification_listeners' instead."
102
- clear_notification_listeners(notification_type)
103
- end
104
-
105
- # Removes notifications for a certain notification type
106
- #
107
- # @param notification_type - one of the constants in NOTIFICATION_TYPES
108
-
109
- def clear_notification_listeners(notification_type)
110
- return nil unless notification_type_valid?(notification_type)
111
-
112
- @notifications[notification_type] = []
113
- @logger.log Logger::INFO, "All callbacks for notification type #{notification_type} have been removed."
114
- end
115
-
116
- # @deprecated Use {#clear_all_notification_listeners} instead.
117
- def clean_all_notifications
118
- @logger.log Logger::WARN, "'clean_all_notifications' is deprecated. Call 'clear_all_notification_listeners' instead."
119
- clear_all_notification_listeners
120
- end
121
-
122
- # Removes all notifications
123
- def clear_all_notification_listeners
124
- @notifications.each_key { |key| @notifications[key] = [] }
125
- end
126
-
127
- # Sends off the notification for the specific event. Uses var args to pass in a
128
- # arbitrary list of parameters according to which notification type was sent
129
- #
130
- # @param notification_type - one of the constants in NOTIFICATION_TYPES
131
- # @param args - list of arguments to the callback
132
- #
133
- # @api no-doc
134
- def send_notifications(notification_type, *args)
135
- return nil unless notification_type_valid?(notification_type)
136
-
137
- @notifications[notification_type].each do |notification|
138
- notification_callback = notification[:callback]
139
- notification_callback.call(*args)
140
- @logger.log Logger::INFO, "Notification #{notification_type} sent successfully."
141
- rescue => e
142
- @logger.log(Logger::ERROR, "Problem calling notify callback. Error: #{e}")
143
- return nil
144
- end
145
- end
146
-
147
- def notification_count(notification_type)
148
- @notifications.include?(notification_type) ? @notifications[notification_type].count : 0
149
- end
150
-
151
- private
152
-
153
- def notification_type_valid?(notification_type)
154
- # Validates notification type
155
-
156
- # Args:
157
- # notification_type: one of the constants in NOTIFICATION_TYPES
158
-
159
- # Returns true if notification_type is valid, false otherwise
160
-
161
- unless notification_type
162
- @logger.log Logger::ERROR, 'Notification type can not be empty.'
163
- return false
164
- end
165
-
166
- unless @notifications.include?(notification_type)
167
- @logger.log Logger::ERROR, 'Invalid notification type.'
168
- @error_handler.handle_error InvalidNotificationType
169
- return false
170
- end
171
- true
172
- end
173
- end
174
- end
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2017-2019, 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
+ module Optimizely
19
+ class NotificationCenter
20
+ # @api no-doc
21
+ attr_reader :notifications, :notification_id
22
+
23
+ NOTIFICATION_TYPES = {
24
+ # DEPRECATED: ACTIVATE notification type is deprecated since relase 3.1.0.
25
+ ACTIVATE: 'ACTIVATE: experiment, user_id, attributes, variation, event',
26
+ DECISION: 'DECISION: type, user_id, attributes, decision_info',
27
+ LOG_EVENT: 'LOG_EVENT: type, log_event',
28
+ OPTIMIZELY_CONFIG_UPDATE: 'optimizely_config_update',
29
+ TRACK: 'TRACK: event_key, user_id, attributes, event_tags, event'
30
+ }.freeze
31
+
32
+ def initialize(logger, error_handler)
33
+ @notification_id = 1
34
+ @notifications = {}
35
+ NOTIFICATION_TYPES.each_value { |value| @notifications[value] = [] }
36
+ @logger = logger
37
+ @error_handler = error_handler
38
+ end
39
+
40
+ # Adds notification callback to the notification center
41
+ #
42
+ # @param notification_type - One of the constants in NOTIFICATION_TYPES
43
+ # @param notification_callback [lambda, Method, Callable] (default: block) - Called when the event is sent
44
+ # @yield Block to be used as callback if callback omitted.
45
+ #
46
+ # @return [notification ID] Used to remove the notification
47
+
48
+ def add_notification_listener(notification_type, notification_callback = nil, &block)
49
+ return nil unless notification_type_valid?(notification_type)
50
+
51
+ if notification_callback && block_given?
52
+ @logger.log Logger::ERROR, 'Callback and block are mutually exclusive.'
53
+ return nil
54
+ end
55
+
56
+ notification_callback ||= block
57
+
58
+ unless notification_callback
59
+ @logger.log Logger::ERROR, 'Callback can not be empty.'
60
+ return nil
61
+ end
62
+
63
+ unless notification_callback.respond_to? :call
64
+ @logger.log Logger::ERROR, 'Invalid notification callback given.'
65
+ return nil
66
+ end
67
+
68
+ @notifications[notification_type].each do |notification|
69
+ return -1 if notification[:callback] == notification_callback
70
+ end
71
+ @notifications[notification_type].push(notification_id: @notification_id, callback: notification_callback)
72
+ notification_id = @notification_id
73
+ @notification_id += 1
74
+ notification_id
75
+ end
76
+
77
+ # Removes previously added notification callback
78
+ #
79
+ # @param notification_id
80
+ #
81
+ # @return [Boolean] true if found and removed, false otherwise
82
+
83
+ def remove_notification_listener(notification_id)
84
+ unless notification_id
85
+ @logger.log Logger::ERROR, 'Notification ID can not be empty.'
86
+ return nil
87
+ end
88
+ @notifications.each_key do |key|
89
+ @notifications[key].each do |notification|
90
+ if notification_id == notification[:notification_id]
91
+ @notifications[key].delete(notification_id: notification_id, callback: notification[:callback])
92
+ return true
93
+ end
94
+ end
95
+ end
96
+ false
97
+ end
98
+
99
+ # @deprecated Use {#clear_notification_listeners} instead.
100
+ def clear_notifications(notification_type)
101
+ @logger.log Logger::WARN, "'clear_notifications' is deprecated. Call 'clear_notification_listeners' instead."
102
+ clear_notification_listeners(notification_type)
103
+ end
104
+
105
+ # Removes notifications for a certain notification type
106
+ #
107
+ # @param notification_type - one of the constants in NOTIFICATION_TYPES
108
+
109
+ def clear_notification_listeners(notification_type)
110
+ return nil unless notification_type_valid?(notification_type)
111
+
112
+ @notifications[notification_type] = []
113
+ @logger.log Logger::INFO, "All callbacks for notification type #{notification_type} have been removed."
114
+ end
115
+
116
+ # @deprecated Use {#clear_all_notification_listeners} instead.
117
+ def clean_all_notifications
118
+ @logger.log Logger::WARN, "'clean_all_notifications' is deprecated. Call 'clear_all_notification_listeners' instead."
119
+ clear_all_notification_listeners
120
+ end
121
+
122
+ # Removes all notifications
123
+ def clear_all_notification_listeners
124
+ @notifications.each_key { |key| @notifications[key] = [] }
125
+ end
126
+
127
+ # Sends off the notification for the specific event. Uses var args to pass in a
128
+ # arbitrary list of parameters according to which notification type was sent
129
+ #
130
+ # @param notification_type - one of the constants in NOTIFICATION_TYPES
131
+ # @param args - list of arguments to the callback
132
+ #
133
+ # @api no-doc
134
+ def send_notifications(notification_type, *args)
135
+ return nil unless notification_type_valid?(notification_type)
136
+
137
+ @notifications[notification_type].each do |notification|
138
+ notification_callback = notification[:callback]
139
+ notification_callback.call(*args)
140
+ @logger.log Logger::INFO, "Notification #{notification_type} sent successfully."
141
+ rescue => e
142
+ @logger.log(Logger::ERROR, "Problem calling notify callback. Error: #{e}")
143
+ return nil
144
+ end
145
+ end
146
+
147
+ def notification_count(notification_type)
148
+ @notifications.include?(notification_type) ? @notifications[notification_type].count : 0
149
+ end
150
+
151
+ private
152
+
153
+ def notification_type_valid?(notification_type)
154
+ # Validates notification type
155
+
156
+ # Args:
157
+ # notification_type: one of the constants in NOTIFICATION_TYPES
158
+
159
+ # Returns true if notification_type is valid, false otherwise
160
+
161
+ unless notification_type
162
+ @logger.log Logger::ERROR, 'Notification type can not be empty.'
163
+ return false
164
+ end
165
+
166
+ unless @notifications.include?(notification_type)
167
+ @logger.log Logger::ERROR, 'Invalid notification type.'
168
+ @error_handler.handle_error InvalidNotificationType
169
+ return false
170
+ end
171
+ true
172
+ end
173
+ end
174
+ end
@@ -1,71 +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
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