vwo-sdk 1.3.0 → 1.5.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 +5 -5
- data/lib/vwo.rb +610 -55
- data/lib/vwo/constants.rb +40 -12
- data/lib/vwo/core/bucketer.rb +7 -9
- data/lib/vwo/core/variation_decider.rb +68 -46
- data/lib/vwo/enums.rb +72 -9
- data/lib/vwo/logger.rb +9 -3
- data/lib/vwo/schemas/settings_file.rb +1 -3
- data/lib/vwo/services/event_dispatcher.rb +3 -5
- data/lib/vwo/services/operand_evaluator.rb +114 -0
- data/lib/vwo/services/segment_evaluator.rb +109 -0
- data/lib/vwo/services/settings_file_manager.rb +4 -6
- data/lib/vwo/services/settings_file_processor.rb +1 -3
- data/lib/vwo/user_storage.rb +1 -3
- data/lib/vwo/utils/campaign.rb +44 -22
- data/lib/vwo/utils/custom_dimensions.rb +49 -0
- data/lib/vwo/utils/feature.rb +56 -0
- data/lib/vwo/utils/function.rb +6 -3
- data/lib/vwo/utils/impression.rb +21 -3
- data/lib/vwo/utils/request.rb +1 -3
- data/lib/vwo/utils/segment.rb +116 -0
- data/lib/vwo/utils/uuid.rb +3 -5
- data/lib/vwo/utils/validations.rb +12 -4
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4e24789df43b6c0c71d71f8211c44ffe12c113e91943b91f8de581214353ccce
|
4
|
+
data.tar.gz: 95ec02ba3df3e88f91bf3f49eb5ab3ab64302b2c664d4d3caa6f569f2e618952
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1dcc6b57a5fd35867d58d71f3f4b73b0da3ca73f01d009493f76b8981562e6d315f004a9095bcd20f230de1bf19559ac0a2ffefaa5befb72abae0e61de8fce6d
|
7
|
+
data.tar.gz: a7b154c4d9ae6c5b681c6cb27b231f3183a21c496e3eb5d33eecf81df0ca1d55922db127db37debe175aa060575f56dfe42ddbff29c0843e69839bcb57e4fc8c
|
data/lib/vwo.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2019 Wingify Software Pvt. Ltd.
|
1
|
+
# Copyright 2019-2020 Wingify Software Pvt. Ltd.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -12,7 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
15
|
+
require 'logger'
|
16
16
|
|
17
17
|
require_relative 'vwo/services/settings_file_manager'
|
18
18
|
require_relative 'vwo/services/event_dispatcher'
|
@@ -21,16 +21,19 @@ require_relative 'vwo/logger'
|
|
21
21
|
require_relative 'vwo/enums'
|
22
22
|
require_relative 'vwo/utils/campaign'
|
23
23
|
require_relative 'vwo/utils/impression'
|
24
|
+
require_relative 'vwo/utils/feature'
|
25
|
+
require_relative 'vwo/utils/custom_dimensions'
|
24
26
|
require_relative 'vwo/constants'
|
25
27
|
require_relative 'vwo/core/variation_decider'
|
26
28
|
|
27
|
-
|
28
29
|
# VWO main file
|
29
30
|
class VWO
|
30
|
-
attr_accessor :is_instance_valid
|
31
|
+
attr_accessor :is_instance_valid, :logger
|
31
32
|
|
32
33
|
include Enums
|
33
34
|
include Utils::Validations
|
35
|
+
include Utils::Feature
|
36
|
+
include Utils::CustomDimensions
|
34
37
|
include Utils::Campaign
|
35
38
|
include Utils::Impression
|
36
39
|
include CONSTANTS
|
@@ -53,13 +56,15 @@ class VWO
|
|
53
56
|
logger = nil,
|
54
57
|
user_storage = nil,
|
55
58
|
is_development_mode = false,
|
56
|
-
settings_file = nil
|
59
|
+
settings_file = nil,
|
60
|
+
options = {}
|
57
61
|
)
|
58
62
|
@account_id = account_id
|
59
63
|
@sdk_key = sdk_key
|
60
64
|
@user_storage = user_storage
|
61
65
|
@is_development_mode = is_development_mode
|
62
66
|
@logger = VWO::Logger.get_instance(logger)
|
67
|
+
@logger.instance.level = options[:log_level] if (0..5).include?(options[:log_level])
|
63
68
|
|
64
69
|
unless valid_settings_file?(get_settings(settings_file))
|
65
70
|
@logger.log(
|
@@ -74,7 +79,10 @@ class VWO
|
|
74
79
|
|
75
80
|
@logger.log(
|
76
81
|
LogLevelEnum::DEBUG,
|
77
|
-
format(
|
82
|
+
format(
|
83
|
+
LogMessageEnum::DebugMessages::VALID_CONFIGURATION,
|
84
|
+
file: FILE
|
85
|
+
)
|
78
86
|
)
|
79
87
|
|
80
88
|
# Process the settings file
|
@@ -87,7 +95,10 @@ class VWO
|
|
87
95
|
if is_development_mode
|
88
96
|
@logger.log(
|
89
97
|
LogLevelEnum::DEBUG,
|
90
|
-
format(
|
98
|
+
format(
|
99
|
+
LogMessageEnum::DebugMessages::SET_DEVELOPMENT_MODE,
|
100
|
+
file: FILE
|
101
|
+
)
|
91
102
|
)
|
92
103
|
end
|
93
104
|
# Assign event dispatcher
|
@@ -96,7 +107,10 @@ class VWO
|
|
96
107
|
# Successfully initialized VWO SDK
|
97
108
|
@logger.log(
|
98
109
|
LogLevelEnum::DEBUG,
|
99
|
-
format(
|
110
|
+
format(
|
111
|
+
LogMessageEnum::DebugMessages::SDK_INITIALIZED,
|
112
|
+
file: FILE
|
113
|
+
)
|
100
114
|
)
|
101
115
|
end
|
102
116
|
|
@@ -122,15 +136,23 @@ class VWO
|
|
122
136
|
#
|
123
137
|
# @param[String] :campaign_key Unique campaign key
|
124
138
|
# @param[String] :user_id ID assigned to a user
|
139
|
+
# @param[Hash] :options Options for custom variables required for segmentation
|
125
140
|
# @return[String|None] If variation is assigned then variation-name
|
126
141
|
# otherwise null in case of user not becoming part
|
127
142
|
|
128
|
-
def activate(campaign_key, user_id)
|
143
|
+
def activate(campaign_key, user_id, options = {})
|
144
|
+
# Retrieve custom variables
|
145
|
+
custom_variables = options['custom_variables'] || options[:custom_variables]
|
146
|
+
|
129
147
|
# Validate input parameters
|
130
|
-
unless valid_string?(campaign_key) && valid_string?(user_id)
|
148
|
+
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables))
|
131
149
|
@logger.log(
|
132
150
|
LogLevelEnum::ERROR,
|
133
|
-
format(
|
151
|
+
format(
|
152
|
+
LogMessageEnum::ErrorMessages::ACTIVATE_API_MISSING_PARAMS,
|
153
|
+
api_name: ApiMethods::ACTIVATE,
|
154
|
+
file: FILE
|
155
|
+
)
|
134
156
|
)
|
135
157
|
return
|
136
158
|
end
|
@@ -138,7 +160,11 @@ class VWO
|
|
138
160
|
unless @is_instance_valid
|
139
161
|
@logger.log(
|
140
162
|
LogLevelEnum::ERROR,
|
141
|
-
format(
|
163
|
+
format(
|
164
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
165
|
+
file: FILE,
|
166
|
+
api_name: ApiMethods::ACTIVATE
|
167
|
+
)
|
142
168
|
)
|
143
169
|
return
|
144
170
|
end
|
@@ -151,23 +177,54 @@ class VWO
|
|
151
177
|
# Log Campaign as invalid
|
152
178
|
@logger.log(
|
153
179
|
LogLevelEnum::ERROR,
|
154
|
-
format(
|
180
|
+
format(
|
181
|
+
LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING,
|
182
|
+
file: FILE,
|
183
|
+
campaign_key: campaign_key,
|
184
|
+
api_name: ApiMethods::ACTIVATE
|
185
|
+
)
|
186
|
+
)
|
187
|
+
return
|
188
|
+
end
|
189
|
+
|
190
|
+
# Get campaign type
|
191
|
+
campaign_type = campaign['type']
|
192
|
+
|
193
|
+
# Validate valid api call
|
194
|
+
if campaign_type != CampaignTypes::VISUAL_AB
|
195
|
+
@logger.log(
|
196
|
+
LogLevelEnum::ERROR,
|
197
|
+
format(
|
198
|
+
LogMessageEnum::ErrorMessages::INVALID_API,
|
199
|
+
file: FILE,
|
200
|
+
api_name: ApiMethods::ACTIVATE,
|
201
|
+
user_id: user_id,
|
202
|
+
campaign_key: campaign_key,
|
203
|
+
campaign_type: campaign_type
|
204
|
+
)
|
155
205
|
)
|
156
206
|
return
|
157
207
|
end
|
158
208
|
|
159
209
|
# Once the matching RUNNING campaign is found, assign the
|
160
210
|
# deterministic variation to the user_id provided
|
161
|
-
|
211
|
+
variation = @variation_decider.get_variation(
|
162
212
|
user_id,
|
163
|
-
campaign
|
213
|
+
campaign,
|
214
|
+
campaign_key,
|
215
|
+
custom_variables
|
164
216
|
)
|
165
217
|
|
166
218
|
# Check if variation_name has been assigned
|
167
|
-
|
219
|
+
if variation.nil?
|
168
220
|
@logger.log(
|
169
221
|
LogLevelEnum::INFO,
|
170
|
-
format(
|
222
|
+
format(
|
223
|
+
LogMessageEnum::InfoMessages::INVALID_VARIATION_KEY,
|
224
|
+
file: FILE,
|
225
|
+
user_id: user_id,
|
226
|
+
campaign_key: campaign_key
|
227
|
+
)
|
171
228
|
)
|
172
229
|
return
|
173
230
|
end
|
@@ -176,11 +233,22 @@ class VWO
|
|
176
233
|
impression = create_impression(
|
177
234
|
@settings_file,
|
178
235
|
campaign['id'],
|
179
|
-
|
236
|
+
variation['id'],
|
180
237
|
user_id
|
181
238
|
)
|
182
239
|
@event_dispatcher.dispatch(impression)
|
183
|
-
|
240
|
+
variation['name']
|
241
|
+
rescue StandardError => e
|
242
|
+
@logger.log(
|
243
|
+
LogLevelEnum::ERROR,
|
244
|
+
format(
|
245
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
246
|
+
file: FILE,
|
247
|
+
api_name: ApiMethods::ACTIVATE,
|
248
|
+
exception: e
|
249
|
+
)
|
250
|
+
)
|
251
|
+
nil
|
184
252
|
end
|
185
253
|
|
186
254
|
# This API method: Gets the variation name assigned for the
|
@@ -195,25 +263,35 @@ class VWO
|
|
195
263
|
#
|
196
264
|
# @param[String] :campaign_key Unique campaign key
|
197
265
|
# @param[String] :user_id ID assigned to a user
|
266
|
+
# @param[Hash] :options Options for custom variables required for segmentation
|
198
267
|
#
|
199
268
|
# @@return[String|Nil] If variation is assigned then variation-name
|
200
269
|
# Otherwise null in case of user not becoming part
|
201
270
|
#
|
202
|
-
def get_variation_name(campaign_key, user_id)
|
203
|
-
#
|
204
|
-
|
205
|
-
|
271
|
+
def get_variation_name(campaign_key, user_id, options = {})
|
272
|
+
# Retrieve custom variables
|
273
|
+
custom_variables = options['custom_variables'] || options[:custom_variables]
|
274
|
+
|
275
|
+
# Validate input parameters
|
276
|
+
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables))
|
206
277
|
@logger.log(
|
207
278
|
LogLevelEnum::ERROR,
|
208
|
-
format(
|
279
|
+
format(
|
280
|
+
LogMessageEnum::ErrorMessages::GET_VARIATION_NAME_API_INVALID_PARAMS,
|
281
|
+
api_name: ApiMethods::GET_VARIATION_NAME,
|
282
|
+
file: FILE
|
283
|
+
)
|
209
284
|
)
|
210
285
|
return
|
211
286
|
end
|
212
|
-
|
213
287
|
unless @is_instance_valid
|
214
288
|
@logger.log(
|
215
289
|
LogLevelEnum::ERROR,
|
216
|
-
format(
|
290
|
+
format(
|
291
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
292
|
+
file: FILE,
|
293
|
+
api_name: ApiMethods::GET_VARIATION_NAME
|
294
|
+
)
|
217
295
|
)
|
218
296
|
return
|
219
297
|
end
|
@@ -225,27 +303,62 @@ class VWO
|
|
225
303
|
if campaign.nil? || campaign['status'] != STATUS_RUNNING
|
226
304
|
@logger.log(
|
227
305
|
LogLevelEnum::ERROR,
|
228
|
-
format(
|
306
|
+
format(
|
307
|
+
LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING,
|
308
|
+
file: FILE,
|
309
|
+
campaign_key: campaign_key,
|
310
|
+
api_name: ApiMethods::GET_VARIATION_NAME
|
311
|
+
)
|
229
312
|
)
|
230
313
|
return
|
231
314
|
end
|
232
315
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
316
|
+
campaign_type = campaign['type']
|
317
|
+
|
318
|
+
if campaign_type == CampaignTypes::FEATURE_ROLLOUT
|
319
|
+
@logger.log(
|
320
|
+
LogLevelEnum::ERROR,
|
321
|
+
format(
|
322
|
+
LogMessageEnum::ErrorMessages.INVALID_API,
|
323
|
+
file: FILE,
|
324
|
+
api_name: ApiMethods::GET_VARIATION_NAME,
|
325
|
+
user_id: user_id,
|
326
|
+
campaign_key: campaign_key,
|
327
|
+
campaign_type: campaign_type
|
328
|
+
)
|
329
|
+
)
|
330
|
+
return
|
331
|
+
end
|
332
|
+
|
333
|
+
variation = @variation_decider.get_variation(user_id, campaign, campaign_key, custom_variables)
|
237
334
|
|
238
335
|
# Check if variation_name has been assigned
|
239
|
-
unless valid_value?(
|
336
|
+
unless valid_value?(variation)
|
240
337
|
# log invalid variation key
|
241
338
|
@logger.log(
|
242
339
|
LogLevelEnum::INFO,
|
243
|
-
format(
|
340
|
+
format(
|
341
|
+
LogMessageEnum::InfoMessages::INVALID_VARIATION_KEY,
|
342
|
+
file: FILE,
|
343
|
+
user_id: user_id,
|
344
|
+
campaign_key: campaign_key
|
345
|
+
)
|
244
346
|
)
|
245
347
|
return
|
246
348
|
end
|
247
349
|
|
248
|
-
|
350
|
+
variation['name']
|
351
|
+
rescue StandardError => e
|
352
|
+
@logger.log(
|
353
|
+
LogLevelEnum::ERROR,
|
354
|
+
format(
|
355
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
356
|
+
file: FILE,
|
357
|
+
api_name: ApiMethods::GET_VARIATION_NAME,
|
358
|
+
exception: e
|
359
|
+
)
|
360
|
+
)
|
361
|
+
nil
|
249
362
|
end
|
250
363
|
|
251
364
|
# This API method: Marks the conversion of the campaign
|
@@ -258,22 +371,30 @@ class VWO
|
|
258
371
|
#
|
259
372
|
# @param[String] :campaign_key Unique campaign key
|
260
373
|
# @param[String] :user_id ID assigned to a user
|
261
|
-
# @param[String] :goal_identifier
|
262
|
-
# @param[
|
374
|
+
# @param[String] :goal_identifier Unique campaign's goal identifier
|
375
|
+
# @param[Array|Hash] :args Contains revenue value and custom variables
|
376
|
+
# @param[Numeric|String] :revenue_value It is the revenue generated on triggering the goal
|
263
377
|
#
|
264
378
|
def track(campaign_key, user_id, goal_identifier, *args)
|
265
379
|
if args[0].is_a?(Hash)
|
266
|
-
revenue_value = args[0]['revenue_value']
|
380
|
+
revenue_value = args[0]['revenue_value'] || args[0][:revenue_value]
|
381
|
+
custom_variables = args[0]['custom_variables'] || args[0][:custom_variables]
|
267
382
|
elsif args.is_a?(Array)
|
268
383
|
revenue_value = args[0]
|
384
|
+
custom_variables = nil
|
269
385
|
end
|
270
386
|
|
271
387
|
# Check for valid args
|
272
|
-
unless valid_string?(campaign_key) && valid_string?(user_id) && valid_string?(goal_identifier)
|
388
|
+
unless valid_string?(campaign_key) && valid_string?(user_id) && valid_string?(goal_identifier) &&
|
389
|
+
(custom_variables.nil? || valid_hash?(custom_variables)) || (revenue_value.nil? || valid_basic_data_type?(revenue_value))
|
273
390
|
# log invalid params
|
274
391
|
@logger.log(
|
275
392
|
LogLevelEnum::ERROR,
|
276
|
-
format(
|
393
|
+
format(
|
394
|
+
LogMessageEnum::ErrorMessages::TRACK_API_INVALID_PARAMS,
|
395
|
+
file: FILE,
|
396
|
+
api_name: ApiMethods.TRACK
|
397
|
+
)
|
277
398
|
)
|
278
399
|
return false
|
279
400
|
end
|
@@ -281,7 +402,11 @@ class VWO
|
|
281
402
|
unless @is_instance_valid
|
282
403
|
@logger.log(
|
283
404
|
LogLevelEnum::ERROR,
|
284
|
-
format(
|
405
|
+
format(
|
406
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
407
|
+
file: FILE,
|
408
|
+
api_name: ApiMethods::TRACK
|
409
|
+
)
|
285
410
|
)
|
286
411
|
return false
|
287
412
|
end
|
@@ -290,33 +415,55 @@ class VWO
|
|
290
415
|
campaign = get_campaign(@settings_file, campaign_key)
|
291
416
|
|
292
417
|
# Validate campaign
|
293
|
-
if campaign.nil? || campaign['status'] != STATUS_RUNNING
|
418
|
+
if campaign.nil? || (campaign['status'] != STATUS_RUNNING)
|
294
419
|
# log error
|
295
420
|
@logger.log(
|
296
421
|
LogLevelEnum::ERROR,
|
297
|
-
format(
|
422
|
+
format(
|
423
|
+
LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING,
|
424
|
+
file: FILE,
|
425
|
+
campaign_key: campaign_key,
|
426
|
+
api_name: ApiMethods::TRACK
|
427
|
+
)
|
298
428
|
)
|
299
429
|
return false
|
300
430
|
end
|
301
431
|
|
302
|
-
|
303
|
-
variation_id, variation_name = @variation_decider.get_variation_allotted(user_id, campaign)
|
432
|
+
campaign_type = campaign['type']
|
304
433
|
|
305
|
-
if
|
306
|
-
|
434
|
+
if campaign_type == CampaignTypes::FEATURE_ROLLOUT
|
435
|
+
@logger.log(
|
436
|
+
LogLevelEnum::ERROR,
|
437
|
+
format(
|
438
|
+
LogMessageEnum::ErrorMessages::INVALID_API,
|
439
|
+
file: FILE,
|
440
|
+
api_name: ApiMethods::TRACK,
|
441
|
+
user_id: user_id,
|
442
|
+
campaign_key: campaign_key,
|
443
|
+
campaign_type: campaign_type
|
444
|
+
)
|
445
|
+
)
|
446
|
+
return false
|
447
|
+
end
|
307
448
|
|
449
|
+
variation = @variation_decider.get_variation(user_id, campaign, campaign_key, custom_variables)
|
450
|
+
|
451
|
+
if variation
|
452
|
+
goal = get_campaign_goal(campaign, goal_identifier)
|
308
453
|
if goal.nil?
|
309
454
|
@logger.log(
|
310
455
|
LogLevelEnum::ERROR,
|
311
456
|
format(
|
312
457
|
LogMessageEnum::ErrorMessages::TRACK_API_GOAL_NOT_FOUND,
|
313
|
-
file: FILE,
|
458
|
+
file: FILE,
|
459
|
+
goal_identifier: goal_identifier,
|
314
460
|
user_id: user_id,
|
315
|
-
campaign_key: campaign_key
|
461
|
+
campaign_key: campaign_key,
|
462
|
+
api_name: ApiMethods::TRACK
|
316
463
|
)
|
317
464
|
)
|
318
465
|
return false
|
319
|
-
elsif goal['type'] ==
|
466
|
+
elsif goal['type'] == GoalTypes::REVENUE && !valid_value?(revenue_value)
|
320
467
|
@logger.log(
|
321
468
|
LogLevelEnum::ERROR,
|
322
469
|
format(
|
@@ -324,25 +471,433 @@ class VWO
|
|
324
471
|
file: FILE,
|
325
472
|
user_id: user_id,
|
326
473
|
goal_identifier: goal_identifier,
|
327
|
-
campaign_key: campaign_key
|
474
|
+
campaign_key: campaign_key,
|
475
|
+
api_name: ApiMethods::TRACK
|
328
476
|
)
|
329
477
|
)
|
330
478
|
return false
|
479
|
+
elsif goal['type'] == GoalTypes::CUSTOM
|
480
|
+
revenue_value = nil
|
331
481
|
end
|
332
|
-
|
333
|
-
revenue_value = nil if goal['type'] == GOALTYPES::CUSTOM
|
334
|
-
|
335
482
|
impression = create_impression(
|
336
483
|
@settings_file,
|
337
|
-
|
338
|
-
|
484
|
+
campaign['id'],
|
485
|
+
variation['id'],
|
339
486
|
user_id,
|
340
487
|
goal['id'],
|
341
488
|
revenue_value
|
342
489
|
)
|
343
490
|
@event_dispatcher.dispatch(impression)
|
491
|
+
|
492
|
+
@logger.log(
|
493
|
+
LogLevelEnum::INFO,
|
494
|
+
format(
|
495
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
|
496
|
+
file: FILE,
|
497
|
+
campaign_id: impression[:experiment_id],
|
498
|
+
user_id: impression[:uId],
|
499
|
+
account_id: impression[:account_id],
|
500
|
+
variation_id: impression[:combination]
|
501
|
+
)
|
502
|
+
)
|
344
503
|
return true
|
345
504
|
end
|
346
505
|
false
|
506
|
+
rescue StandardError => e
|
507
|
+
@logger.log(
|
508
|
+
LogLevelEnum::ERROR,
|
509
|
+
format(
|
510
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
511
|
+
file: FILE,
|
512
|
+
api_name: ApiMethods::TRACK,
|
513
|
+
exception: e
|
514
|
+
)
|
515
|
+
)
|
516
|
+
false
|
517
|
+
end
|
518
|
+
|
519
|
+
# This API method: Identifies whether the user becomes a part of feature rollout/test or not.
|
520
|
+
# 1. Validates the arguments being passed
|
521
|
+
# 2. Checks if user is eligible to get bucketed into the feature test/rollout,
|
522
|
+
# 3. Assigns the deterministic variation to the user(based on userId),
|
523
|
+
# If user becomes part of feature test/rollout
|
524
|
+
# If UserStorage is used, it will look into it for the variation and if found, no further processing is done
|
525
|
+
#
|
526
|
+
# @param[String] :campaign_key Unique campaign key
|
527
|
+
# @param[String] :user_id ID assigned to a user
|
528
|
+
# @param[Hash] :custom_variables Pass it through options as custom_variables={}
|
529
|
+
#
|
530
|
+
# @return[Boolean] true if user becomes part of feature test/rollout, otherwise false.
|
531
|
+
|
532
|
+
def feature_enabled?(campaign_key, user_id, options = {})
|
533
|
+
# Retrieve custom variables
|
534
|
+
custom_variables = options['custom_variables'] || options[:custom_variables]
|
535
|
+
|
536
|
+
# Validate input parameters
|
537
|
+
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables))
|
538
|
+
@logger.log(
|
539
|
+
LogLevelEnum::ERROR,
|
540
|
+
format(
|
541
|
+
LogMessageEnum::ErrorMessages::IS_FEATURE_ENABLED_API_INVALID_PARAMS,
|
542
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED,
|
543
|
+
file: FILE
|
544
|
+
)
|
545
|
+
)
|
546
|
+
return false
|
547
|
+
end
|
548
|
+
unless @is_instance_valid
|
549
|
+
@logger.log(
|
550
|
+
LogLevelEnum::ERROR,
|
551
|
+
format(
|
552
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
553
|
+
file: FILE,
|
554
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
555
|
+
)
|
556
|
+
)
|
557
|
+
return false
|
558
|
+
end
|
559
|
+
|
560
|
+
# Get the campaign settings
|
561
|
+
campaign = get_campaign(@settings_file, campaign_key)
|
562
|
+
|
563
|
+
# Validate campaign
|
564
|
+
unless campaign && campaign['status'] == STATUS_RUNNING
|
565
|
+
# log error
|
566
|
+
@logger.log(
|
567
|
+
LogLevelEnum::ERROR,
|
568
|
+
format(
|
569
|
+
LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING,
|
570
|
+
file: FILE,
|
571
|
+
campaign_key: campaign_key,
|
572
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
573
|
+
)
|
574
|
+
)
|
575
|
+
return false
|
576
|
+
end
|
577
|
+
|
578
|
+
# Validate campaign_type
|
579
|
+
campaign_type = campaign['type']
|
580
|
+
|
581
|
+
if campaign_type == CampaignTypes::VISUAL_AB
|
582
|
+
@logger.log(
|
583
|
+
LogLevelEnum::ERROR,
|
584
|
+
format(
|
585
|
+
LogMessageEnum::ErrorMessages::INVALID_API,
|
586
|
+
file: FILE,
|
587
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED,
|
588
|
+
user_id: user_id,
|
589
|
+
campaign_key: campaign_key,
|
590
|
+
campaign_type: campaign_type
|
591
|
+
)
|
592
|
+
)
|
593
|
+
return false
|
594
|
+
end
|
595
|
+
|
596
|
+
# Get variation
|
597
|
+
variation = @variation_decider.get_variation(user_id, campaign, campaign_key, custom_variables)
|
598
|
+
|
599
|
+
# If no variation, did not become part of feature_test/rollout
|
600
|
+
return false unless variation
|
601
|
+
|
602
|
+
# if campaign type is feature_test Send track call to server
|
603
|
+
if campaign_type == CampaignTypes::FEATURE_TEST
|
604
|
+
impression = create_impression(
|
605
|
+
@settings_file,
|
606
|
+
campaign['id'],
|
607
|
+
variation['id'],
|
608
|
+
user_id
|
609
|
+
)
|
610
|
+
|
611
|
+
@event_dispatcher.dispatch(impression)
|
612
|
+
@logger.log(
|
613
|
+
LogLevelEnum::INFO,
|
614
|
+
format(
|
615
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
|
616
|
+
file: FILE,
|
617
|
+
campaign_id: impression[:experiment_id],
|
618
|
+
user_id: impression[:uId],
|
619
|
+
account_id: impression[:account_id],
|
620
|
+
variation_id: impression[:combination]
|
621
|
+
)
|
622
|
+
)
|
623
|
+
result = variation['isFeatureEnabled']
|
624
|
+
if result
|
625
|
+
@logger.log(
|
626
|
+
LogLevelEnum::INFO,
|
627
|
+
format(
|
628
|
+
LogMessageEnum::InfoMessages::FEATURE_ENABLED_FOR_USER,
|
629
|
+
file: FILE,
|
630
|
+
user_id: user_id,
|
631
|
+
feature_key: campaign_key,
|
632
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
633
|
+
)
|
634
|
+
)
|
635
|
+
else
|
636
|
+
@logger.log(
|
637
|
+
LogLevelEnum::INFO,
|
638
|
+
format(
|
639
|
+
LogMessageEnum::InfoMessages::FEATURE_NOT_ENABLED_FOR_USER,
|
640
|
+
file: FILE,
|
641
|
+
user_id: user_id,
|
642
|
+
feature_key: campaign_key,
|
643
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
644
|
+
)
|
645
|
+
)
|
646
|
+
end
|
647
|
+
return result
|
648
|
+
end
|
649
|
+
true
|
650
|
+
rescue StandardError => e
|
651
|
+
@logger.log(
|
652
|
+
LogLevelEnum::ERROR,
|
653
|
+
format(
|
654
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
655
|
+
file: FILE,
|
656
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED,
|
657
|
+
exception: e
|
658
|
+
)
|
659
|
+
)
|
660
|
+
false
|
661
|
+
end
|
662
|
+
|
663
|
+
# Returns the feature variable corresponding to the variable_key
|
664
|
+
# passed. It typecasts the value to the corresponding value type
|
665
|
+
# found in settings_file
|
666
|
+
#
|
667
|
+
# 1. Validates the arguments being passed
|
668
|
+
# 2. Checks if user is eligible to get bucketed into the feature test/rollout,
|
669
|
+
# 3. Assigns the deterministic variation to the user(based on userId),
|
670
|
+
# If user becomes part of campaign
|
671
|
+
# If UserStorage is used, it will look into it for the variation and if found, no further processing is done
|
672
|
+
# 4. Retrieves the corresponding variable from variation assigned.
|
673
|
+
#
|
674
|
+
# @param[String] :campaign_key Unique campaign key
|
675
|
+
# @param[String] :variable_key Variable key
|
676
|
+
# @param[String] :user_id ID assigned to a user
|
677
|
+
# @param[Hash] :custom_variables Pass it through options as custom_variables={}
|
678
|
+
#
|
679
|
+
# @return[Boolean, String, Integer, Float, nil) If variation is assigned then variable corresponding to variation assigned else nil
|
680
|
+
#
|
681
|
+
|
682
|
+
def get_feature_variable_value(campaign_key, variable_key, user_id, options = {})
|
683
|
+
# Retrieve custom variables
|
684
|
+
custom_variables = options['custom_variables'] || options[:custom_variables]
|
685
|
+
|
686
|
+
unless valid_string?(campaign_key) && valid_string?(variable_key) && valid_string?(user_id) &&
|
687
|
+
(custom_variables.nil? || valid_hash?(custom_variables))
|
688
|
+
@logger.log(
|
689
|
+
LogLevelEnum::ERROR,
|
690
|
+
format(
|
691
|
+
LogMessageEnum::ErrorMessages::GET_FEATURE_VARIABLE_VALUE_API_INVALID_PARAMS,
|
692
|
+
file: FILE,
|
693
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
694
|
+
)
|
695
|
+
)
|
696
|
+
return
|
697
|
+
end
|
698
|
+
|
699
|
+
unless @is_instance_valid
|
700
|
+
@logger.log(
|
701
|
+
LogLevelEnum::ERROR,
|
702
|
+
format(
|
703
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
704
|
+
file: FILE,
|
705
|
+
api_name: ApiMethods.GET_FEATURE_VARIABLE_VALUE
|
706
|
+
)
|
707
|
+
)
|
708
|
+
return
|
709
|
+
end
|
710
|
+
|
711
|
+
# Get the campaign settings
|
712
|
+
campaign = get_campaign(@settings_file, campaign_key)
|
713
|
+
|
714
|
+
# Validate campaign
|
715
|
+
unless campaign && campaign['status'] == STATUS_RUNNING
|
716
|
+
# log error
|
717
|
+
@logger.log(
|
718
|
+
LogLevelEnum::ERROR,
|
719
|
+
format(
|
720
|
+
LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING,
|
721
|
+
file: FILE,
|
722
|
+
campaign_key: campaign_key,
|
723
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
724
|
+
)
|
725
|
+
)
|
726
|
+
return
|
727
|
+
end
|
728
|
+
|
729
|
+
campaign_type = campaign['type']
|
730
|
+
|
731
|
+
if campaign_type == CampaignTypes::VISUAL_AB
|
732
|
+
@logger.log(
|
733
|
+
LogLevelEnum::ERROR,
|
734
|
+
format(
|
735
|
+
LogMessageEnum::ErrorMessages::INVALID_API,
|
736
|
+
file: FILE,
|
737
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE,
|
738
|
+
campaign_key: campaign_key,
|
739
|
+
campaign_type: campaign_type,
|
740
|
+
user_id: user_id
|
741
|
+
)
|
742
|
+
)
|
743
|
+
return
|
744
|
+
end
|
745
|
+
|
746
|
+
variation = @variation_decider.get_variation(user_id, campaign, campaign_key, custom_variables)
|
747
|
+
|
748
|
+
# Check if variation has been assigned to user
|
749
|
+
return unless variation
|
750
|
+
|
751
|
+
if campaign_type == CampaignTypes::FEATURE_ROLLOUT
|
752
|
+
variables = campaign['variables']
|
753
|
+
elsif campaign_type == CampaignTypes::FEATURE_TEST
|
754
|
+
if !variation['isFeatureEnabled']
|
755
|
+
@logger.log(
|
756
|
+
LogLevelEnum::INFO,
|
757
|
+
format(
|
758
|
+
LogMessageEnum::InfoMessages::FEATURE_NOT_ENABLED_FOR_USER,
|
759
|
+
file: FILE,
|
760
|
+
feature_key: campaign_key,
|
761
|
+
user_id: user_id,
|
762
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
763
|
+
)
|
764
|
+
)
|
765
|
+
variation = get_control_variation(campaign)
|
766
|
+
else
|
767
|
+
@logger.log(
|
768
|
+
LogLevelEnum::INFO,
|
769
|
+
format(
|
770
|
+
LogMessageEnum::InfoMessages::FEATURE_ENABLED_FOR_USER,
|
771
|
+
file: FILE,
|
772
|
+
feature_key: campaign_key,
|
773
|
+
user_id: user_id,
|
774
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
775
|
+
)
|
776
|
+
)
|
777
|
+
end
|
778
|
+
variables = variation['variables']
|
779
|
+
end
|
780
|
+
variable = get_variable(variables, variable_key)
|
781
|
+
|
782
|
+
unless variable
|
783
|
+
# Log variable not found
|
784
|
+
@logger.log(
|
785
|
+
LogLevelEnum::ERROR,
|
786
|
+
format(
|
787
|
+
LogMessageEnum::ErrorMessages::VARIABLE_NOT_FOUND,
|
788
|
+
file: FILE,
|
789
|
+
variable_key: variable_key,
|
790
|
+
campaign_key: campaign_key,
|
791
|
+
campaign_type: campaign_type,
|
792
|
+
user_id: user_id,
|
793
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
794
|
+
)
|
795
|
+
)
|
796
|
+
return
|
797
|
+
end
|
798
|
+
|
799
|
+
@logger.log(
|
800
|
+
LogLevelEnum::INFO,
|
801
|
+
format(
|
802
|
+
LogMessageEnum::InfoMessages::VARIABLE_FOUND,
|
803
|
+
file: FILE,
|
804
|
+
variable_key: variable_key,
|
805
|
+
variable_value: variable['value'],
|
806
|
+
campaign_key: campaign_key,
|
807
|
+
campaign_type: campaign_type,
|
808
|
+
user_id: user_id,
|
809
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
810
|
+
)
|
811
|
+
)
|
812
|
+
get_type_casted_feature_value(variable['value'], variable['type'])
|
813
|
+
rescue StandardError => e
|
814
|
+
@logger.log(
|
815
|
+
LogLevelEnum::ERROR,
|
816
|
+
format(
|
817
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
818
|
+
file: FILE,
|
819
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE,
|
820
|
+
exception: e
|
821
|
+
)
|
822
|
+
)
|
823
|
+
nil
|
824
|
+
end
|
825
|
+
|
826
|
+
# This API method: Makes a call to our server to store the tag_values
|
827
|
+
# 1. Validates the arguments being passed
|
828
|
+
# 2. Send a call to our server
|
829
|
+
# @param[String] :tag_key key name of the tag
|
830
|
+
# @param[String] :tag_value Value of the tag
|
831
|
+
# @param[String] :user_id ID of the user for which value should be stored
|
832
|
+
# @return true if call is made successfully, else false
|
833
|
+
|
834
|
+
def push(tag_key, tag_value, user_id)
|
835
|
+
unless valid_string?(tag_key) && valid_string?(tag_value) && valid_string?(user_id)
|
836
|
+
@logger.log(
|
837
|
+
LogLevelEnum::ERROR,
|
838
|
+
format(
|
839
|
+
LogMessageEnum::ErrorMessages::PUSH_API_INVALID_PARAMS,
|
840
|
+
file: FILE,
|
841
|
+
api_name: ApiMethods::PUSH
|
842
|
+
)
|
843
|
+
)
|
844
|
+
return false
|
845
|
+
end
|
846
|
+
|
847
|
+
if tag_key.length > PushApi::TAG_KEY_LENGTH
|
848
|
+
@logger.log(
|
849
|
+
LogLevelEnum::ERROR,
|
850
|
+
format(
|
851
|
+
LogMessageEnum::ErrorMessages::TAG_KEY_LENGTH_EXCEEDED,
|
852
|
+
file: FILE,
|
853
|
+
user_id: user_id,
|
854
|
+
tag_key: tag_key,
|
855
|
+
api_name: ApiMethods::PUSH
|
856
|
+
)
|
857
|
+
)
|
858
|
+
return false
|
859
|
+
end
|
860
|
+
|
861
|
+
if tag_value.length > PushApi::TAG_VALUE_LENGTH
|
862
|
+
@logger.log(
|
863
|
+
LogLevelEnum::ERROR,
|
864
|
+
format(
|
865
|
+
LogMessageEnum::ErrorMessages::TAG_VALUE_LENGTH_EXCEEDED,
|
866
|
+
file: FILE,
|
867
|
+
user_id: user_id,
|
868
|
+
tag_value: tag_value,
|
869
|
+
api_name: ApiMethods::PUSH
|
870
|
+
)
|
871
|
+
)
|
872
|
+
return false
|
873
|
+
end
|
874
|
+
|
875
|
+
impression = get_url_params(@settings_file, tag_key, tag_value, user_id)
|
876
|
+
|
877
|
+
@event_dispatcher.dispatch(impression)
|
878
|
+
|
879
|
+
@logger.log(
|
880
|
+
LogLevelEnum::INFO,
|
881
|
+
format(
|
882
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_PUSH_API,
|
883
|
+
file: FILE,
|
884
|
+
u: impression['u'],
|
885
|
+
user_id: impression['uId'],
|
886
|
+
account_id: impression['account_id'],
|
887
|
+
tags: impression['tags']
|
888
|
+
)
|
889
|
+
)
|
890
|
+
true
|
891
|
+
rescue StandardError => e
|
892
|
+
@logger.log(
|
893
|
+
LogLevelEnum::ERROR,
|
894
|
+
format(
|
895
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
896
|
+
file: FILE,
|
897
|
+
api_name: ApiMethods::PUSH,
|
898
|
+
exception: e
|
899
|
+
)
|
900
|
+
)
|
901
|
+
false
|
347
902
|
end
|
348
903
|
end
|