optimizely-sdk 3.6.0 → 3.9.0
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 +18 -39
- data/lib/optimizely/bucketer.rb +35 -27
- data/lib/optimizely/condition_tree_evaluator.rb +2 -0
- data/lib/optimizely/config/datafile_project_config.rb +97 -14
- data/lib/optimizely/decide/optimizely_decide_option.rb +28 -0
- data/lib/optimizely/decide/optimizely_decision.rb +60 -0
- data/lib/optimizely/decide/optimizely_decision_message.rb +26 -0
- data/lib/optimizely/decision_service.rb +164 -141
- data/lib/optimizely/event/entity/decision.rb +6 -4
- data/lib/optimizely/event/entity/impression_event.rb +4 -2
- data/lib/optimizely/event/event_factory.rb +4 -3
- data/lib/optimizely/event/user_event_factory.rb +4 -3
- data/lib/optimizely/helpers/constants.rb +1 -0
- data/lib/optimizely/optimizely_config.rb +180 -25
- data/lib/optimizely/optimizely_user_context.rb +107 -0
- data/lib/optimizely/project_config.rb +14 -2
- data/lib/optimizely/version.rb +1 -1
- data/lib/optimizely.rb +245 -18
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3502b43c8fae064fda3e1b237786a0285103d4ce9383a9d64cfc7b34f3f8c849
|
4
|
+
data.tar.gz: 9b843fc3a7999e640f329812aeeb8e26474b5b85f29d27c0647ba7c3685cb73f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a244307de60cf4a7e9b4e90bd0282e071e310701c09b4314a5f011f9f97a1f638205dc9e3de59611c247315314bfd58a0521690bbc3fa876575f3ef8fa917306
|
7
|
+
data.tar.gz: b68df42eef86c3a4e1fd22f2b1b4cd3060df4ade0c9125862443164735778a16415af958a4002fba7adcdf078d1552a5ffef446bac22e7a269a5984445d1fd50
|
data/lib/optimizely/audience.rb
CHANGED
@@ -38,6 +38,7 @@ module Optimizely
|
|
38
38
|
# This defaults to experiment['key'].
|
39
39
|
#
|
40
40
|
# Returns boolean representing if user satisfies audience conditions for the audiences or not.
|
41
|
+
decide_reasons = []
|
41
42
|
logging_hash ||= 'EXPERIMENT_AUDIENCE_EVALUATION_LOGS'
|
42
43
|
logging_key ||= experiment['key']
|
43
44
|
|
@@ -45,26 +46,15 @@ module Optimizely
|
|
45
46
|
|
46
47
|
audience_conditions = experiment['audienceConditions'] || experiment['audienceIds']
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
format(
|
51
|
-
logs_hash['EVALUATING_AUDIENCES_COMBINED'],
|
52
|
-
logging_key,
|
53
|
-
audience_conditions
|
54
|
-
)
|
55
|
-
)
|
49
|
+
message = format(logs_hash['EVALUATING_AUDIENCES_COMBINED'], logging_key, audience_conditions)
|
50
|
+
logger.log(Logger::DEBUG, message)
|
56
51
|
|
57
52
|
# Return true if there are no audiences
|
58
53
|
if audience_conditions.empty?
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
logging_key,
|
64
|
-
'TRUE'
|
65
|
-
)
|
66
|
-
)
|
67
|
-
return true
|
54
|
+
message = format(logs_hash['AUDIENCE_EVALUATION_RESULT_COMBINED'], logging_key, 'TRUE')
|
55
|
+
logger.log(Logger::INFO, message)
|
56
|
+
decide_reasons.push(message)
|
57
|
+
return true, decide_reasons
|
68
58
|
end
|
69
59
|
|
70
60
|
attributes ||= {}
|
@@ -80,39 +70,28 @@ module Optimizely
|
|
80
70
|
return nil unless audience
|
81
71
|
|
82
72
|
audience_conditions = audience['conditions']
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
logs_hash['EVALUATING_AUDIENCE'],
|
87
|
-
audience_id,
|
88
|
-
audience_conditions
|
89
|
-
)
|
90
|
-
)
|
73
|
+
message = format(logs_hash['EVALUATING_AUDIENCE'], audience_id, audience_conditions)
|
74
|
+
logger.log(Logger::DEBUG, message)
|
75
|
+
decide_reasons.push(message)
|
91
76
|
|
92
77
|
audience_conditions = JSON.parse(audience_conditions) if audience_conditions.is_a?(String)
|
93
78
|
result = ConditionTreeEvaluator.evaluate(audience_conditions, evaluate_custom_attr)
|
94
79
|
result_str = result.nil? ? 'UNKNOWN' : result.to_s.upcase
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
80
|
+
message = format(logs_hash['AUDIENCE_EVALUATION_RESULT'], audience_id, result_str)
|
81
|
+
logger.log(Logger::DEBUG, message)
|
82
|
+
decide_reasons.push(message)
|
83
|
+
|
99
84
|
result
|
100
85
|
end
|
101
86
|
|
102
87
|
eval_result = ConditionTreeEvaluator.evaluate(audience_conditions, evaluate_audience)
|
103
|
-
|
104
88
|
eval_result ||= false
|
105
89
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
logs_hash['AUDIENCE_EVALUATION_RESULT_COMBINED'],
|
110
|
-
logging_key,
|
111
|
-
eval_result.to_s.upcase
|
112
|
-
)
|
113
|
-
)
|
90
|
+
message = format(logs_hash['AUDIENCE_EVALUATION_RESULT_COMBINED'], logging_key, eval_result.to_s.upcase)
|
91
|
+
logger.log(Logger::INFO, message)
|
92
|
+
decide_reasons.push(message)
|
114
93
|
|
115
|
-
eval_result
|
94
|
+
[eval_result, decide_reasons]
|
116
95
|
end
|
117
96
|
end
|
118
97
|
end
|
data/lib/optimizely/bucketer.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2016-2017, 2019-
|
4
|
+
# Copyright 2016-2017, 2019-2021 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.
|
@@ -44,7 +44,9 @@ module Optimizely
|
|
44
44
|
# user_id - String ID for user.
|
45
45
|
#
|
46
46
|
# Returns variation in which visitor with ID user_id has been placed. Nil if no variation.
|
47
|
-
return nil if experiment.nil?
|
47
|
+
return nil, [] if experiment.nil?
|
48
|
+
|
49
|
+
decide_reasons = []
|
48
50
|
|
49
51
|
# check if experiment is in a group; if so, check if user is bucketed into specified experiment
|
50
52
|
# this will not affect evaluation of rollout rules.
|
@@ -55,46 +57,49 @@ module Optimizely
|
|
55
57
|
group = project_config.group_id_map.fetch(group_id)
|
56
58
|
if Helpers::Group.random_policy?(group)
|
57
59
|
traffic_allocations = group.fetch('trafficAllocation')
|
58
|
-
bucketed_experiment_id = find_bucket(bucketing_id, user_id, group_id, traffic_allocations)
|
60
|
+
bucketed_experiment_id, find_bucket_reasons = find_bucket(bucketing_id, user_id, group_id, traffic_allocations)
|
61
|
+
decide_reasons.push(*find_bucket_reasons)
|
62
|
+
|
59
63
|
# return if the user is not bucketed into any experiment
|
60
64
|
unless bucketed_experiment_id
|
61
|
-
|
62
|
-
|
65
|
+
message = "User '#{user_id}' is in no experiment."
|
66
|
+
@logger.log(Logger::INFO, message)
|
67
|
+
decide_reasons.push(message)
|
68
|
+
return nil, decide_reasons
|
63
69
|
end
|
64
70
|
|
65
71
|
# return if the user is bucketed into a different experiment than the one specified
|
66
72
|
if bucketed_experiment_id != experiment_id
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
return nil
|
73
|
+
message = "User '#{user_id}' is not in experiment '#{experiment_key}' of group #{group_id}."
|
74
|
+
@logger.log(Logger::INFO, message)
|
75
|
+
decide_reasons.push(message)
|
76
|
+
return nil, decide_reasons
|
72
77
|
end
|
73
78
|
|
74
79
|
# continue bucketing if the user is bucketed into the experiment specified
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
)
|
80
|
+
message = "User '#{user_id}' is in experiment '#{experiment_key}' of group #{group_id}."
|
81
|
+
@logger.log(Logger::INFO, message)
|
82
|
+
decide_reasons.push(message)
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
82
86
|
traffic_allocations = experiment['trafficAllocation']
|
83
|
-
variation_id = find_bucket(bucketing_id, user_id, experiment_id, traffic_allocations)
|
87
|
+
variation_id, find_bucket_reasons = find_bucket(bucketing_id, user_id, experiment_id, traffic_allocations)
|
88
|
+
decide_reasons.push(*find_bucket_reasons)
|
89
|
+
|
84
90
|
if variation_id && variation_id != ''
|
85
|
-
variation = project_config.
|
86
|
-
return variation
|
91
|
+
variation = project_config.get_variation_from_id_by_experiment_id(experiment_id, variation_id)
|
92
|
+
return variation, decide_reasons
|
87
93
|
end
|
88
94
|
|
89
95
|
# Handle the case when the traffic range is empty due to sticky bucketing
|
90
96
|
if variation_id == ''
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
)
|
97
|
+
message = 'Bucketed into an empty traffic range. Returning nil.'
|
98
|
+
@logger.log(Logger::DEBUG, message)
|
99
|
+
decide_reasons.push(message)
|
95
100
|
end
|
96
101
|
|
97
|
-
nil
|
102
|
+
[nil, decide_reasons]
|
98
103
|
end
|
99
104
|
|
100
105
|
def find_bucket(bucketing_id, user_id, parent_id, traffic_allocations)
|
@@ -105,21 +110,24 @@ module Optimizely
|
|
105
110
|
# parent_id - String entity ID to use for bucketing ID
|
106
111
|
# traffic_allocations - Array of traffic allocations
|
107
112
|
#
|
108
|
-
# Returns entity ID corresponding to the provided bucket value
|
113
|
+
# Returns and array of two values where first value is the entity ID corresponding to the provided bucket value
|
114
|
+
# or nil if no match is found. The second value contains the array of reasons stating how the deicision was taken
|
115
|
+
decide_reasons = []
|
109
116
|
bucketing_key = format(BUCKETING_ID_TEMPLATE, bucketing_id: bucketing_id, entity_id: parent_id)
|
110
117
|
bucket_value = generate_bucket_value(bucketing_key)
|
111
|
-
|
112
|
-
|
118
|
+
|
119
|
+
message = "Assigned bucket #{bucket_value} to user '#{user_id}' with bucketing ID: '#{bucketing_id}'."
|
120
|
+
@logger.log(Logger::DEBUG, message)
|
113
121
|
|
114
122
|
traffic_allocations.each do |traffic_allocation|
|
115
123
|
current_end_of_range = traffic_allocation['endOfRange']
|
116
124
|
if bucket_value < current_end_of_range
|
117
125
|
entity_id = traffic_allocation['entityId']
|
118
|
-
return entity_id
|
126
|
+
return entity_id, decide_reasons
|
119
127
|
end
|
120
128
|
end
|
121
129
|
|
122
|
-
nil
|
130
|
+
[nil, decide_reasons]
|
123
131
|
end
|
124
132
|
|
125
133
|
private
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019-
|
3
|
+
# Copyright 2019-2021, Optimizely and contributors
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -38,8 +38,11 @@ module Optimizely
|
|
38
38
|
attr_reader :anonymize_ip
|
39
39
|
attr_reader :bot_filtering
|
40
40
|
attr_reader :revision
|
41
|
+
attr_reader :sdk_key
|
42
|
+
attr_reader :environment_key
|
41
43
|
attr_reader :rollouts
|
42
44
|
attr_reader :version
|
45
|
+
attr_reader :send_flag_decisions
|
43
46
|
|
44
47
|
attr_reader :attribute_key_map
|
45
48
|
attr_reader :audience_id_map
|
@@ -51,10 +54,12 @@ module Optimizely
|
|
51
54
|
attr_reader :feature_variable_key_map
|
52
55
|
attr_reader :group_id_map
|
53
56
|
attr_reader :rollout_id_map
|
54
|
-
attr_reader :
|
57
|
+
attr_reader :rollout_experiment_id_map
|
55
58
|
attr_reader :variation_id_map
|
56
59
|
attr_reader :variation_id_to_variable_usage_map
|
57
60
|
attr_reader :variation_key_map
|
61
|
+
attr_reader :variation_id_map_by_experiment_id
|
62
|
+
attr_reader :variation_key_map_by_experiment_id
|
58
63
|
|
59
64
|
def initialize(datafile, logger, error_handler)
|
60
65
|
# ProjectConfig init method to fetch and set project config data
|
@@ -82,7 +87,10 @@ module Optimizely
|
|
82
87
|
@anonymize_ip = config.key?('anonymizeIP') ? config['anonymizeIP'] : false
|
83
88
|
@bot_filtering = config['botFiltering']
|
84
89
|
@revision = config['revision']
|
90
|
+
@sdk_key = config.fetch('sdkKey', '')
|
91
|
+
@environment_key = config.fetch('environmentKey', '')
|
85
92
|
@rollouts = config.fetch('rollouts', [])
|
93
|
+
@send_flag_decisions = config.fetch('sendFlagDecisions', false)
|
86
94
|
|
87
95
|
# Json type is represented in datafile as a subtype of string for the sake of backwards compatibility.
|
88
96
|
# Converting it to a first-class json type while creating Project Config
|
@@ -111,9 +119,11 @@ module Optimizely
|
|
111
119
|
@audience_id_map = @audience_id_map.merge(generate_key_map(@typed_audiences, 'id')) unless @typed_audiences.empty?
|
112
120
|
@variation_id_map = {}
|
113
121
|
@variation_key_map = {}
|
122
|
+
@variation_id_map_by_experiment_id = {}
|
123
|
+
@variation_key_map_by_experiment_id = {}
|
114
124
|
@variation_id_to_variable_usage_map = {}
|
115
125
|
@variation_id_to_experiment_map = {}
|
116
|
-
@
|
126
|
+
@experiment_id_map.each_value do |exp|
|
117
127
|
# Excludes experiments from rollouts
|
118
128
|
variations = exp.fetch('variations')
|
119
129
|
variations.each do |variation|
|
@@ -123,13 +133,13 @@ module Optimizely
|
|
123
133
|
end
|
124
134
|
@rollout_id_map = generate_key_map(@rollouts, 'id')
|
125
135
|
# split out the experiment key map for rollouts
|
126
|
-
@
|
136
|
+
@rollout_experiment_id_map = {}
|
127
137
|
@rollout_id_map.each_value do |rollout|
|
128
138
|
exps = rollout.fetch('experiments')
|
129
|
-
@
|
139
|
+
@rollout_experiment_id_map = @rollout_experiment_id_map.merge(generate_key_map(exps, 'id'))
|
130
140
|
end
|
131
|
-
@all_experiments = @
|
132
|
-
@all_experiments.each do |
|
141
|
+
@all_experiments = @experiment_id_map.merge(@rollout_experiment_id_map)
|
142
|
+
@all_experiments.each do |id, exp|
|
133
143
|
variations = exp.fetch('variations')
|
134
144
|
variations.each do |variation|
|
135
145
|
variation_id = variation['id']
|
@@ -139,8 +149,10 @@ module Optimizely
|
|
139
149
|
|
140
150
|
@variation_id_to_variable_usage_map[variation_id] = generate_key_map(variation_variables, 'id')
|
141
151
|
end
|
142
|
-
@variation_id_map[key] = generate_key_map(variations, 'id')
|
143
|
-
@variation_key_map[key] = generate_key_map(variations, 'key')
|
152
|
+
@variation_id_map[exp['key']] = generate_key_map(variations, 'id')
|
153
|
+
@variation_key_map[exp['key']] = generate_key_map(variations, 'key')
|
154
|
+
@variation_id_map_by_experiment_id[id] = generate_key_map(variations, 'id')
|
155
|
+
@variation_key_map_by_experiment_id[id] = generate_key_map(variations, 'key')
|
144
156
|
end
|
145
157
|
@feature_flag_key_map = generate_key_map(@feature_flags, 'key')
|
146
158
|
@experiment_feature_map = {}
|
@@ -207,6 +219,21 @@ module Optimizely
|
|
207
219
|
nil
|
208
220
|
end
|
209
221
|
|
222
|
+
def get_experiment_from_id(experiment_id)
|
223
|
+
# Retrieves experiment ID for a given key
|
224
|
+
#
|
225
|
+
# experiment_id - String id representing the experiment
|
226
|
+
#
|
227
|
+
# Returns Experiment or nil if not found
|
228
|
+
|
229
|
+
experiment = @experiment_id_map[experiment_id]
|
230
|
+
return experiment if experiment
|
231
|
+
|
232
|
+
@logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
|
233
|
+
@error_handler.handle_error InvalidExperimentError
|
234
|
+
nil
|
235
|
+
end
|
236
|
+
|
210
237
|
def get_experiment_key(experiment_id)
|
211
238
|
# Retrieves experiment key for a given ID.
|
212
239
|
#
|
@@ -275,6 +302,52 @@ module Optimizely
|
|
275
302
|
nil
|
276
303
|
end
|
277
304
|
|
305
|
+
def get_variation_from_id_by_experiment_id(experiment_id, variation_id)
|
306
|
+
# Get variation given experiment ID and variation ID
|
307
|
+
#
|
308
|
+
# experiment_id - ID representing parent experiment of variation
|
309
|
+
# variation_id - ID of the variation
|
310
|
+
#
|
311
|
+
# Returns the variation or nil if not found
|
312
|
+
|
313
|
+
variation_id_map_by_experiment_id = @variation_id_map_by_experiment_id[experiment_id]
|
314
|
+
if variation_id_map_by_experiment_id
|
315
|
+
variation = variation_id_map_by_experiment_id[variation_id]
|
316
|
+
return variation if variation
|
317
|
+
|
318
|
+
@logger.log Logger::ERROR, "Variation id '#{variation_id}' is not in datafile."
|
319
|
+
@error_handler.handle_error InvalidVariationError
|
320
|
+
return nil
|
321
|
+
end
|
322
|
+
|
323
|
+
@logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
|
324
|
+
@error_handler.handle_error InvalidExperimentError
|
325
|
+
nil
|
326
|
+
end
|
327
|
+
|
328
|
+
def get_variation_id_from_key_by_experiment_id(experiment_id, variation_key)
|
329
|
+
# Get variation given experiment ID and variation key
|
330
|
+
#
|
331
|
+
# experiment_id - ID representing parent experiment of variation
|
332
|
+
# variation_key - Key of the variation
|
333
|
+
#
|
334
|
+
# Returns the variation or nil if not found
|
335
|
+
|
336
|
+
variation_key_map = @variation_key_map_by_experiment_id[experiment_id]
|
337
|
+
if variation_key_map
|
338
|
+
variation = variation_key_map[variation_key]
|
339
|
+
return variation['id'] if variation
|
340
|
+
|
341
|
+
@logger.log Logger::ERROR, "Variation key '#{variation_key}' is not in datafile."
|
342
|
+
@error_handler.handle_error InvalidVariationError
|
343
|
+
return nil
|
344
|
+
end
|
345
|
+
|
346
|
+
@logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
|
347
|
+
@error_handler.handle_error InvalidExperimentError
|
348
|
+
nil
|
349
|
+
end
|
350
|
+
|
278
351
|
def get_variation_id_from_key(experiment_key, variation_key)
|
279
352
|
# Get variation ID given experiment key and variation key
|
280
353
|
#
|
@@ -298,17 +371,17 @@ module Optimizely
|
|
298
371
|
nil
|
299
372
|
end
|
300
373
|
|
301
|
-
def get_whitelisted_variations(
|
302
|
-
# Retrieves whitelisted variations for a given experiment
|
374
|
+
def get_whitelisted_variations(experiment_id)
|
375
|
+
# Retrieves whitelisted variations for a given experiment id
|
303
376
|
#
|
304
|
-
#
|
377
|
+
# experiment_id - String id representing the experiment
|
305
378
|
#
|
306
379
|
# Returns whitelisted variations for the experiment or nil
|
307
380
|
|
308
|
-
experiment = @
|
381
|
+
experiment = @experiment_id_map[experiment_id]
|
309
382
|
return experiment['forcedVariations'] if experiment
|
310
383
|
|
311
|
-
@logger.log Logger::ERROR, "Experiment
|
384
|
+
@logger.log Logger::ERROR, "Experiment ID '#{experiment_id}' is not in datafile."
|
312
385
|
@error_handler.handle_error InvalidExperimentError
|
313
386
|
end
|
314
387
|
|
@@ -409,6 +482,16 @@ module Optimizely
|
|
409
482
|
@experiment_feature_map.key?(experiment_id)
|
410
483
|
end
|
411
484
|
|
485
|
+
def rollout_experiment?(experiment_id)
|
486
|
+
# Determines if given experiment is a rollout test.
|
487
|
+
#
|
488
|
+
# experiment_id - String experiment ID
|
489
|
+
#
|
490
|
+
# Returns true if experiment belongs to any rollout,
|
491
|
+
# false otherwise.
|
492
|
+
@rollout_experiment_id_map.key?(experiment_id)
|
493
|
+
end
|
494
|
+
|
412
495
|
private
|
413
496
|
|
414
497
|
def generate_key_map(array, key)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2020, 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
|
+
module Decide
|
20
|
+
module OptimizelyDecideOption
|
21
|
+
DISABLE_DECISION_EVENT = 'DISABLE_DECISION_EVENT'
|
22
|
+
ENABLED_FLAGS_ONLY = 'ENABLED_FLAGS_ONLY'
|
23
|
+
IGNORE_USER_PROFILE_SERVICE = 'IGNORE_USER_PROFILE_SERVICE'
|
24
|
+
INCLUDE_REASONS = 'INCLUDE_REASONS'
|
25
|
+
EXCLUDE_VARIABLES = 'EXCLUDE_VARIABLES'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|