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.
- checksums.yaml +4 -4
- data/LICENSE +202 -202
- data/lib/optimizely/audience.rb +127 -127
- data/lib/optimizely/bucketer.rb +156 -156
- data/lib/optimizely/condition_tree_evaluator.rb +123 -123
- data/lib/optimizely/config/datafile_project_config.rb +558 -558
- data/lib/optimizely/config/proxy_config.rb +34 -34
- data/lib/optimizely/config_manager/async_scheduler.rb +95 -95
- data/lib/optimizely/config_manager/http_project_config_manager.rb +340 -340
- data/lib/optimizely/config_manager/project_config_manager.rb +25 -25
- data/lib/optimizely/config_manager/static_project_config_manager.rb +55 -55
- data/lib/optimizely/decide/optimizely_decide_option.rb +28 -28
- data/lib/optimizely/decide/optimizely_decision.rb +60 -60
- data/lib/optimizely/decide/optimizely_decision_message.rb +26 -26
- data/lib/optimizely/decision_service.rb +563 -563
- data/lib/optimizely/error_handler.rb +39 -39
- data/lib/optimizely/event/batch_event_processor.rb +235 -235
- data/lib/optimizely/event/entity/conversion_event.rb +44 -44
- data/lib/optimizely/event/entity/decision.rb +38 -38
- data/lib/optimizely/event/entity/event_batch.rb +86 -86
- data/lib/optimizely/event/entity/event_context.rb +50 -50
- data/lib/optimizely/event/entity/impression_event.rb +48 -48
- data/lib/optimizely/event/entity/snapshot.rb +33 -33
- data/lib/optimizely/event/entity/snapshot_event.rb +48 -48
- data/lib/optimizely/event/entity/user_event.rb +22 -22
- data/lib/optimizely/event/entity/visitor.rb +36 -36
- data/lib/optimizely/event/entity/visitor_attribute.rb +38 -38
- data/lib/optimizely/event/event_factory.rb +156 -156
- data/lib/optimizely/event/event_processor.rb +25 -25
- data/lib/optimizely/event/forwarding_event_processor.rb +44 -44
- data/lib/optimizely/event/user_event_factory.rb +88 -88
- data/lib/optimizely/event_builder.rb +221 -221
- data/lib/optimizely/event_dispatcher.rb +69 -69
- data/lib/optimizely/exceptions.rb +193 -193
- data/lib/optimizely/helpers/constants.rb +459 -459
- data/lib/optimizely/helpers/date_time_utils.rb +30 -30
- data/lib/optimizely/helpers/event_tag_utils.rb +132 -132
- data/lib/optimizely/helpers/group.rb +31 -31
- data/lib/optimizely/helpers/http_utils.rb +68 -68
- data/lib/optimizely/helpers/sdk_settings.rb +61 -61
- data/lib/optimizely/helpers/validator.rb +236 -236
- data/lib/optimizely/helpers/variable_type.rb +67 -67
- data/lib/optimizely/logger.rb +46 -46
- data/lib/optimizely/notification_center.rb +174 -174
- data/lib/optimizely/notification_center_registry.rb +71 -71
- data/lib/optimizely/odp/lru_cache.rb +114 -114
- data/lib/optimizely/odp/odp_config.rb +102 -102
- data/lib/optimizely/odp/odp_event.rb +75 -75
- data/lib/optimizely/odp/odp_event_api_manager.rb +70 -70
- data/lib/optimizely/odp/odp_event_manager.rb +286 -286
- data/lib/optimizely/odp/odp_manager.rb +159 -159
- data/lib/optimizely/odp/odp_segment_api_manager.rb +122 -122
- data/lib/optimizely/odp/odp_segment_manager.rb +97 -97
- data/lib/optimizely/optimizely_config.rb +273 -273
- data/lib/optimizely/optimizely_factory.rb +184 -184
- data/lib/optimizely/optimizely_user_context.rb +238 -238
- data/lib/optimizely/params.rb +31 -31
- data/lib/optimizely/project_config.rb +99 -99
- data/lib/optimizely/semantic_version.rb +166 -166
- data/lib/optimizely/user_condition_evaluator.rb +391 -391
- data/lib/optimizely/user_profile_service.rb +35 -35
- data/lib/optimizely/version.rb +21 -21
- data/lib/optimizely.rb +1262 -1262
- metadata +7 -5
@@ -1,273 +1,273 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copyright 2019-2022, Optimizely and contributors
|
4
|
-
#
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
-
# you may not use this file except in compliance with the License.
|
7
|
-
# You may obtain a copy of the License at
|
8
|
-
#
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
-
#
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
-
# See the License for the specific language governing permissions and
|
15
|
-
# limitations under the License.
|
16
|
-
#
|
17
|
-
|
18
|
-
module Optimizely
|
19
|
-
require 'json'
|
20
|
-
class OptimizelyConfig
|
21
|
-
include Optimizely::ConditionTreeEvaluator
|
22
|
-
def initialize(project_config, logger = nil)
|
23
|
-
@project_config = project_config
|
24
|
-
@logger = logger || NoOpLogger.new
|
25
|
-
@rollouts = @project_config.rollouts
|
26
|
-
@audiences = []
|
27
|
-
audience_id_lookup_dict = {}
|
28
|
-
|
29
|
-
@project_config.typed_audiences.each do |typed_audience|
|
30
|
-
@audiences.push(
|
31
|
-
'id' => typed_audience['id'],
|
32
|
-
'name' => typed_audience['name'],
|
33
|
-
'conditions' => typed_audience['conditions'].to_json
|
34
|
-
)
|
35
|
-
audience_id_lookup_dict[typed_audience['id']] = typed_audience['id']
|
36
|
-
end
|
37
|
-
|
38
|
-
@project_config.audiences.each do |audience|
|
39
|
-
next unless !audience_id_lookup_dict.key?(audience['id']) && (audience['id'] != '$opt_dummy_audience')
|
40
|
-
|
41
|
-
@audiences.push(
|
42
|
-
'id' => audience['id'],
|
43
|
-
'name' => audience['name'],
|
44
|
-
'conditions' => audience['conditions']
|
45
|
-
)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def config
|
50
|
-
experiments_map_object = experiments_map
|
51
|
-
features_map = get_features_map(experiments_id_map)
|
52
|
-
{
|
53
|
-
'sdkKey' => @project_config.sdk_key,
|
54
|
-
'datafile' => @project_config.datafile,
|
55
|
-
# This experimentsMap is for experiments of legacy projects only.
|
56
|
-
# For flag projects, experiment keys are not guaranteed to be unique
|
57
|
-
# across multiple flags, so this map may not include all experiments
|
58
|
-
# when keys conflict. Use experimentRules and deliveryRules instead.
|
59
|
-
'experimentsMap' => experiments_map_object,
|
60
|
-
'featuresMap' => features_map,
|
61
|
-
'revision' => @project_config.revision,
|
62
|
-
'attributes' => get_attributes_list(@project_config.attributes),
|
63
|
-
'audiences' => @audiences,
|
64
|
-
'events' => get_events_list(@project_config.events),
|
65
|
-
'environmentKey' => @project_config.environment_key
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def experiments_id_map
|
72
|
-
feature_variables_map = feature_variable_map
|
73
|
-
audiences_id_map = audiences_map
|
74
|
-
@project_config.experiments.reduce({}) do |experiments_map, experiment|
|
75
|
-
feature_id = @project_config.experiment_feature_map.fetch(experiment['id'], []).first
|
76
|
-
experiments_map.update(
|
77
|
-
experiment['id'] => {
|
78
|
-
'id' => experiment['id'],
|
79
|
-
'key' => experiment['key'],
|
80
|
-
'variationsMap' => get_variation_map(feature_id, experiment, feature_variables_map),
|
81
|
-
'audiences' => replace_ids_with_names(experiment.fetch('audienceConditions', []), audiences_id_map) || ''
|
82
|
-
}
|
83
|
-
)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def audiences_map
|
88
|
-
@audiences.reduce({}) do |audiences_map, optly_audience|
|
89
|
-
audiences_map.update(optly_audience['id'] => optly_audience['name'])
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def experiments_map
|
94
|
-
experiments_id_map.values.reduce({}) do |experiments_key_map, experiment|
|
95
|
-
@logger.log(Logger::WARN, "Duplicate experiment keys found in datafile: #{experiment['key']}") if experiments_key_map.key? experiment['key']
|
96
|
-
experiments_key_map.update(experiment['key'] => experiment)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def feature_variable_map
|
101
|
-
@project_config.feature_flags.reduce({}) do |result_map, feature|
|
102
|
-
result_map.update(feature['id'] => feature['variables'])
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def get_variation_map(feature_id, experiment, feature_variables_map)
|
107
|
-
experiment['variations'].reduce({}) do |variations_map, variation|
|
108
|
-
variation_object = {
|
109
|
-
'id' => variation['id'],
|
110
|
-
'key' => variation['key'],
|
111
|
-
'featureEnabled' => variation['featureEnabled'],
|
112
|
-
'variablesMap' => get_merged_variables_map(variation, feature_id, feature_variables_map)
|
113
|
-
}
|
114
|
-
variations_map.update(variation['key'] => variation_object)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
# Merges feature key and type from feature variables to variation variables.
|
119
|
-
def get_merged_variables_map(variation, feature_id, feature_variables_map)
|
120
|
-
return {} unless feature_id
|
121
|
-
|
122
|
-
feature_variables = feature_variables_map[feature_id]
|
123
|
-
# temporary variation variables map to get values to merge.
|
124
|
-
temp_variables_id_map = {}
|
125
|
-
if variation['variables']
|
126
|
-
temp_variables_id_map = variation['variables'].reduce({}) do |variables_map, variable|
|
127
|
-
variables_map.update(
|
128
|
-
variable['id'] => {
|
129
|
-
'id' => variable['id'],
|
130
|
-
'value' => variable['value']
|
131
|
-
}
|
132
|
-
)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
feature_variables.reduce({}) do |variables_map, feature_variable|
|
136
|
-
variation_variable = temp_variables_id_map[feature_variable['id']]
|
137
|
-
variable_value = variation['featureEnabled'] && variation_variable ? variation_variable['value'] : feature_variable['defaultValue']
|
138
|
-
variables_map.update(
|
139
|
-
feature_variable['key'] => {
|
140
|
-
'id' => feature_variable['id'],
|
141
|
-
'key' => feature_variable['key'],
|
142
|
-
'type' => feature_variable['type'],
|
143
|
-
'value' => variable_value
|
144
|
-
}
|
145
|
-
)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def get_features_map(all_experiments_map)
|
150
|
-
@project_config.feature_flags.reduce({}) do |features_map, feature|
|
151
|
-
delivery_rules = get_delivery_rules(@rollouts, feature['rolloutId'], feature['id'])
|
152
|
-
features_map.update(
|
153
|
-
feature['key'] => {
|
154
|
-
'id' => feature['id'],
|
155
|
-
'key' => feature['key'],
|
156
|
-
# This experimentsMap is deprecated. Use experimentRules and deliveryRules instead.
|
157
|
-
'experimentsMap' => feature['experimentIds'].reduce({}) do |experiments_map, experiment_id|
|
158
|
-
experiment_key = @project_config.experiment_id_map[experiment_id]['key']
|
159
|
-
experiments_map.update(experiment_key => experiments_id_map[experiment_id])
|
160
|
-
end,
|
161
|
-
'variablesMap' => feature['variables'].reduce({}) do |variables, variable|
|
162
|
-
variables.update(
|
163
|
-
variable['key'] => {
|
164
|
-
'id' => variable['id'],
|
165
|
-
'key' => variable['key'],
|
166
|
-
'type' => variable['type'],
|
167
|
-
'value' => variable['defaultValue']
|
168
|
-
}
|
169
|
-
)
|
170
|
-
end,
|
171
|
-
'experimentRules' => feature['experimentIds'].reduce([]) do |experiments_map, experiment_id|
|
172
|
-
experiments_map.push(all_experiments_map[experiment_id])
|
173
|
-
end,
|
174
|
-
'deliveryRules' => delivery_rules
|
175
|
-
}
|
176
|
-
)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def get_attributes_list(attributes)
|
181
|
-
attributes.map do |attribute|
|
182
|
-
{
|
183
|
-
'id' => attribute['id'],
|
184
|
-
'key' => attribute['key']
|
185
|
-
}
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def get_events_list(events)
|
190
|
-
events.map do |event|
|
191
|
-
{
|
192
|
-
'id' => event['id'],
|
193
|
-
'key' => event['key'],
|
194
|
-
'experimentIds' => event['experimentIds']
|
195
|
-
}
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def lookup_name_from_id(audience_id, audiences_map)
|
200
|
-
audiences_map[audience_id] || audience_id
|
201
|
-
end
|
202
|
-
|
203
|
-
def stringify_conditions(conditions, audiences_map)
|
204
|
-
operand = 'OR'
|
205
|
-
conditions_str = ''
|
206
|
-
length = conditions.length
|
207
|
-
return '' if length.zero?
|
208
|
-
return "\"#{lookup_name_from_id(conditions[0], audiences_map)}\"" if length == 1 && !OPERATORS.include?(conditions[0])
|
209
|
-
|
210
|
-
# Edge cases for lengths 0, 1 or 2
|
211
|
-
if length == 2 && OPERATORS.include?(conditions[0]) && !conditions[1].is_a?(Array) && !OPERATORS.include?(conditions[1])
|
212
|
-
return "\"#{lookup_name_from_id(conditions[1], audiences_map)}\"" if conditions[0] != 'not'
|
213
|
-
|
214
|
-
return "#{conditions[0].upcase} \"#{lookup_name_from_id(conditions[1], audiences_map)}\""
|
215
|
-
|
216
|
-
end
|
217
|
-
if length > 1
|
218
|
-
(0..length - 1).each do |n|
|
219
|
-
# Operand is handled here and made Upper Case
|
220
|
-
if OPERATORS.include?(conditions[n])
|
221
|
-
operand = conditions[n].upcase
|
222
|
-
# Check if element is a list or not
|
223
|
-
elsif conditions[n].is_a?(Array)
|
224
|
-
# Check if at the end or not to determine where to add the operand
|
225
|
-
# Recursive call to call stringify on embedded list
|
226
|
-
conditions_str += if n + 1 < length
|
227
|
-
"(#{stringify_conditions(conditions[n], audiences_map)}) "
|
228
|
-
else
|
229
|
-
"#{operand} (#{stringify_conditions(conditions[n], audiences_map)})"
|
230
|
-
end
|
231
|
-
# If the item is not a list, we process as an audience ID and retrieve the name
|
232
|
-
else
|
233
|
-
audience_name = lookup_name_from_id(conditions[n], audiences_map)
|
234
|
-
unless audience_name.nil?
|
235
|
-
# Below handles all cases for one ID or greater
|
236
|
-
conditions_str += if n + 1 < length - 1
|
237
|
-
"\"#{audience_name}\" #{operand} "
|
238
|
-
elsif n + 1 == length
|
239
|
-
"#{operand} \"#{audience_name}\""
|
240
|
-
else
|
241
|
-
"\"#{audience_name}\" "
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
conditions_str || ''
|
248
|
-
end
|
249
|
-
|
250
|
-
def replace_ids_with_names(conditions, audiences_map)
|
251
|
-
!conditions.empty? ? stringify_conditions(conditions, audiences_map) : ''
|
252
|
-
end
|
253
|
-
|
254
|
-
def get_delivery_rules(rollouts, rollout_id, feature_id)
|
255
|
-
audiences_id_map = audiences_map
|
256
|
-
feature_variables_map = feature_variable_map
|
257
|
-
rollout = rollouts.select { |selected_rollout| selected_rollout['id'] == rollout_id }
|
258
|
-
if rollout.any?
|
259
|
-
rollout = rollout[0]
|
260
|
-
experiments = rollout['experiments']
|
261
|
-
return experiments.map do |experiment|
|
262
|
-
{
|
263
|
-
'id' => experiment['id'],
|
264
|
-
'key' => experiment['key'],
|
265
|
-
'variationsMap' => get_variation_map(feature_id, experiment, feature_variables_map),
|
266
|
-
'audiences' => replace_ids_with_names(experiment.fetch('audienceConditions', []), audiences_id_map) || ''
|
267
|
-
}
|
268
|
-
end
|
269
|
-
end
|
270
|
-
[]
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019-2022, Optimizely and contributors
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
module Optimizely
|
19
|
+
require 'json'
|
20
|
+
class OptimizelyConfig
|
21
|
+
include Optimizely::ConditionTreeEvaluator
|
22
|
+
def initialize(project_config, logger = nil)
|
23
|
+
@project_config = project_config
|
24
|
+
@logger = logger || NoOpLogger.new
|
25
|
+
@rollouts = @project_config.rollouts
|
26
|
+
@audiences = []
|
27
|
+
audience_id_lookup_dict = {}
|
28
|
+
|
29
|
+
@project_config.typed_audiences.each do |typed_audience|
|
30
|
+
@audiences.push(
|
31
|
+
'id' => typed_audience['id'],
|
32
|
+
'name' => typed_audience['name'],
|
33
|
+
'conditions' => typed_audience['conditions'].to_json
|
34
|
+
)
|
35
|
+
audience_id_lookup_dict[typed_audience['id']] = typed_audience['id']
|
36
|
+
end
|
37
|
+
|
38
|
+
@project_config.audiences.each do |audience|
|
39
|
+
next unless !audience_id_lookup_dict.key?(audience['id']) && (audience['id'] != '$opt_dummy_audience')
|
40
|
+
|
41
|
+
@audiences.push(
|
42
|
+
'id' => audience['id'],
|
43
|
+
'name' => audience['name'],
|
44
|
+
'conditions' => audience['conditions']
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def config
|
50
|
+
experiments_map_object = experiments_map
|
51
|
+
features_map = get_features_map(experiments_id_map)
|
52
|
+
{
|
53
|
+
'sdkKey' => @project_config.sdk_key,
|
54
|
+
'datafile' => @project_config.datafile,
|
55
|
+
# This experimentsMap is for experiments of legacy projects only.
|
56
|
+
# For flag projects, experiment keys are not guaranteed to be unique
|
57
|
+
# across multiple flags, so this map may not include all experiments
|
58
|
+
# when keys conflict. Use experimentRules and deliveryRules instead.
|
59
|
+
'experimentsMap' => experiments_map_object,
|
60
|
+
'featuresMap' => features_map,
|
61
|
+
'revision' => @project_config.revision,
|
62
|
+
'attributes' => get_attributes_list(@project_config.attributes),
|
63
|
+
'audiences' => @audiences,
|
64
|
+
'events' => get_events_list(@project_config.events),
|
65
|
+
'environmentKey' => @project_config.environment_key
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def experiments_id_map
|
72
|
+
feature_variables_map = feature_variable_map
|
73
|
+
audiences_id_map = audiences_map
|
74
|
+
@project_config.experiments.reduce({}) do |experiments_map, experiment|
|
75
|
+
feature_id = @project_config.experiment_feature_map.fetch(experiment['id'], []).first
|
76
|
+
experiments_map.update(
|
77
|
+
experiment['id'] => {
|
78
|
+
'id' => experiment['id'],
|
79
|
+
'key' => experiment['key'],
|
80
|
+
'variationsMap' => get_variation_map(feature_id, experiment, feature_variables_map),
|
81
|
+
'audiences' => replace_ids_with_names(experiment.fetch('audienceConditions', []), audiences_id_map) || ''
|
82
|
+
}
|
83
|
+
)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def audiences_map
|
88
|
+
@audiences.reduce({}) do |audiences_map, optly_audience|
|
89
|
+
audiences_map.update(optly_audience['id'] => optly_audience['name'])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def experiments_map
|
94
|
+
experiments_id_map.values.reduce({}) do |experiments_key_map, experiment|
|
95
|
+
@logger.log(Logger::WARN, "Duplicate experiment keys found in datafile: #{experiment['key']}") if experiments_key_map.key? experiment['key']
|
96
|
+
experiments_key_map.update(experiment['key'] => experiment)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def feature_variable_map
|
101
|
+
@project_config.feature_flags.reduce({}) do |result_map, feature|
|
102
|
+
result_map.update(feature['id'] => feature['variables'])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_variation_map(feature_id, experiment, feature_variables_map)
|
107
|
+
experiment['variations'].reduce({}) do |variations_map, variation|
|
108
|
+
variation_object = {
|
109
|
+
'id' => variation['id'],
|
110
|
+
'key' => variation['key'],
|
111
|
+
'featureEnabled' => variation['featureEnabled'],
|
112
|
+
'variablesMap' => get_merged_variables_map(variation, feature_id, feature_variables_map)
|
113
|
+
}
|
114
|
+
variations_map.update(variation['key'] => variation_object)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Merges feature key and type from feature variables to variation variables.
|
119
|
+
def get_merged_variables_map(variation, feature_id, feature_variables_map)
|
120
|
+
return {} unless feature_id
|
121
|
+
|
122
|
+
feature_variables = feature_variables_map[feature_id]
|
123
|
+
# temporary variation variables map to get values to merge.
|
124
|
+
temp_variables_id_map = {}
|
125
|
+
if variation['variables']
|
126
|
+
temp_variables_id_map = variation['variables'].reduce({}) do |variables_map, variable|
|
127
|
+
variables_map.update(
|
128
|
+
variable['id'] => {
|
129
|
+
'id' => variable['id'],
|
130
|
+
'value' => variable['value']
|
131
|
+
}
|
132
|
+
)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
feature_variables.reduce({}) do |variables_map, feature_variable|
|
136
|
+
variation_variable = temp_variables_id_map[feature_variable['id']]
|
137
|
+
variable_value = variation['featureEnabled'] && variation_variable ? variation_variable['value'] : feature_variable['defaultValue']
|
138
|
+
variables_map.update(
|
139
|
+
feature_variable['key'] => {
|
140
|
+
'id' => feature_variable['id'],
|
141
|
+
'key' => feature_variable['key'],
|
142
|
+
'type' => feature_variable['type'],
|
143
|
+
'value' => variable_value
|
144
|
+
}
|
145
|
+
)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def get_features_map(all_experiments_map)
|
150
|
+
@project_config.feature_flags.reduce({}) do |features_map, feature|
|
151
|
+
delivery_rules = get_delivery_rules(@rollouts, feature['rolloutId'], feature['id'])
|
152
|
+
features_map.update(
|
153
|
+
feature['key'] => {
|
154
|
+
'id' => feature['id'],
|
155
|
+
'key' => feature['key'],
|
156
|
+
# This experimentsMap is deprecated. Use experimentRules and deliveryRules instead.
|
157
|
+
'experimentsMap' => feature['experimentIds'].reduce({}) do |experiments_map, experiment_id|
|
158
|
+
experiment_key = @project_config.experiment_id_map[experiment_id]['key']
|
159
|
+
experiments_map.update(experiment_key => experiments_id_map[experiment_id])
|
160
|
+
end,
|
161
|
+
'variablesMap' => feature['variables'].reduce({}) do |variables, variable|
|
162
|
+
variables.update(
|
163
|
+
variable['key'] => {
|
164
|
+
'id' => variable['id'],
|
165
|
+
'key' => variable['key'],
|
166
|
+
'type' => variable['type'],
|
167
|
+
'value' => variable['defaultValue']
|
168
|
+
}
|
169
|
+
)
|
170
|
+
end,
|
171
|
+
'experimentRules' => feature['experimentIds'].reduce([]) do |experiments_map, experiment_id|
|
172
|
+
experiments_map.push(all_experiments_map[experiment_id])
|
173
|
+
end,
|
174
|
+
'deliveryRules' => delivery_rules
|
175
|
+
}
|
176
|
+
)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def get_attributes_list(attributes)
|
181
|
+
attributes.map do |attribute|
|
182
|
+
{
|
183
|
+
'id' => attribute['id'],
|
184
|
+
'key' => attribute['key']
|
185
|
+
}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def get_events_list(events)
|
190
|
+
events.map do |event|
|
191
|
+
{
|
192
|
+
'id' => event['id'],
|
193
|
+
'key' => event['key'],
|
194
|
+
'experimentIds' => event['experimentIds']
|
195
|
+
}
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def lookup_name_from_id(audience_id, audiences_map)
|
200
|
+
audiences_map[audience_id] || audience_id
|
201
|
+
end
|
202
|
+
|
203
|
+
def stringify_conditions(conditions, audiences_map)
|
204
|
+
operand = 'OR'
|
205
|
+
conditions_str = ''
|
206
|
+
length = conditions.length
|
207
|
+
return '' if length.zero?
|
208
|
+
return "\"#{lookup_name_from_id(conditions[0], audiences_map)}\"" if length == 1 && !OPERATORS.include?(conditions[0])
|
209
|
+
|
210
|
+
# Edge cases for lengths 0, 1 or 2
|
211
|
+
if length == 2 && OPERATORS.include?(conditions[0]) && !conditions[1].is_a?(Array) && !OPERATORS.include?(conditions[1])
|
212
|
+
return "\"#{lookup_name_from_id(conditions[1], audiences_map)}\"" if conditions[0] != 'not'
|
213
|
+
|
214
|
+
return "#{conditions[0].upcase} \"#{lookup_name_from_id(conditions[1], audiences_map)}\""
|
215
|
+
|
216
|
+
end
|
217
|
+
if length > 1
|
218
|
+
(0..length - 1).each do |n|
|
219
|
+
# Operand is handled here and made Upper Case
|
220
|
+
if OPERATORS.include?(conditions[n])
|
221
|
+
operand = conditions[n].upcase
|
222
|
+
# Check if element is a list or not
|
223
|
+
elsif conditions[n].is_a?(Array)
|
224
|
+
# Check if at the end or not to determine where to add the operand
|
225
|
+
# Recursive call to call stringify on embedded list
|
226
|
+
conditions_str += if n + 1 < length
|
227
|
+
"(#{stringify_conditions(conditions[n], audiences_map)}) "
|
228
|
+
else
|
229
|
+
"#{operand} (#{stringify_conditions(conditions[n], audiences_map)})"
|
230
|
+
end
|
231
|
+
# If the item is not a list, we process as an audience ID and retrieve the name
|
232
|
+
else
|
233
|
+
audience_name = lookup_name_from_id(conditions[n], audiences_map)
|
234
|
+
unless audience_name.nil?
|
235
|
+
# Below handles all cases for one ID or greater
|
236
|
+
conditions_str += if n + 1 < length - 1
|
237
|
+
"\"#{audience_name}\" #{operand} "
|
238
|
+
elsif n + 1 == length
|
239
|
+
"#{operand} \"#{audience_name}\""
|
240
|
+
else
|
241
|
+
"\"#{audience_name}\" "
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
conditions_str || ''
|
248
|
+
end
|
249
|
+
|
250
|
+
def replace_ids_with_names(conditions, audiences_map)
|
251
|
+
!conditions.empty? ? stringify_conditions(conditions, audiences_map) : ''
|
252
|
+
end
|
253
|
+
|
254
|
+
def get_delivery_rules(rollouts, rollout_id, feature_id)
|
255
|
+
audiences_id_map = audiences_map
|
256
|
+
feature_variables_map = feature_variable_map
|
257
|
+
rollout = rollouts.select { |selected_rollout| selected_rollout['id'] == rollout_id }
|
258
|
+
if rollout.any?
|
259
|
+
rollout = rollout[0]
|
260
|
+
experiments = rollout['experiments']
|
261
|
+
return experiments.map do |experiment|
|
262
|
+
{
|
263
|
+
'id' => experiment['id'],
|
264
|
+
'key' => experiment['key'],
|
265
|
+
'variationsMap' => get_variation_map(feature_id, experiment, feature_variables_map),
|
266
|
+
'audiences' => replace_ids_with_names(experiment.fetch('audienceConditions', []), audiences_id_map) || ''
|
267
|
+
}
|
268
|
+
end
|
269
|
+
end
|
270
|
+
[]
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|