vwo-ruby-sdk 1.0.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 +7 -0
- data/lib/vwo/bucketing_service.rb +158 -0
- data/lib/vwo/common/campaign_utils.rb +111 -0
- data/lib/vwo/common/constants.rb +50 -0
- data/lib/vwo/common/enums.rb +107 -0
- data/lib/vwo/common/function_utils.rb +25 -0
- data/lib/vwo/common/impression_utils.rb +84 -0
- data/lib/vwo/common/requests.rb +17 -0
- data/lib/vwo/common/schemas/settings_file.rb +90 -0
- data/lib/vwo/common/utils.rb +11 -0
- data/lib/vwo/common/uuid_utils.rb +79 -0
- data/lib/vwo/common/validations.rb +67 -0
- data/lib/vwo/custom_logger.rb +23 -0
- data/lib/vwo/decision_service.rb +305 -0
- data/lib/vwo/event_dispatcher.rb +67 -0
- data/lib/vwo/get_settings.rb +65 -0
- data/lib/vwo/project_config_manager.rb +40 -0
- data/lib/vwo/user_profile.rb +23 -0
- data/lib/vwo.rb +349 -0
- metadata +118 -0
data/lib/vwo.rb
ADDED
@@ -0,0 +1,349 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'vwo/get_settings'
|
4
|
+
require_relative 'vwo/custom_logger'
|
5
|
+
require_relative 'vwo/common/utils'
|
6
|
+
require_relative 'vwo/common/enums'
|
7
|
+
require_relative 'vwo/common/campaign_utils'
|
8
|
+
require_relative 'vwo/common/impression_utils'
|
9
|
+
require_relative 'vwo/common/constants'
|
10
|
+
require_relative 'vwo/project_config_manager'
|
11
|
+
require_relative 'vwo/decision_service'
|
12
|
+
require_relative 'vwo/event_dispatcher'
|
13
|
+
|
14
|
+
# Class encapsulating all SDK functionality.
|
15
|
+
class VWO
|
16
|
+
attr_accessor :is_valid
|
17
|
+
|
18
|
+
include Common::Enums
|
19
|
+
include Common::Validations
|
20
|
+
include Common::CampaignUtils
|
21
|
+
include Common::ImpressionUtils
|
22
|
+
include Common::CONSTANTS
|
23
|
+
|
24
|
+
FILE = FileNameEnum::VWO
|
25
|
+
|
26
|
+
# VWO init method for managing custom projects.
|
27
|
+
# Setting various services on the instance
|
28
|
+
# To be accessible by its member functions
|
29
|
+
#
|
30
|
+
# @param[Numeric|String] :account_id Account Id in VWO
|
31
|
+
# @param[String] :sdk_key Unique sdk key for user,
|
32
|
+
# can be retrieved from our website
|
33
|
+
# @param[Object] :logger Optional component which provides a log method
|
34
|
+
# to log messages. By default everything would be logged.
|
35
|
+
# @param[Object] :user_profile_service Optional component which provides
|
36
|
+
# methods to store and manage user profiles.
|
37
|
+
# @param[Boolean] :is_development_mode To specify whether the request
|
38
|
+
# to our server should be sent or not.
|
39
|
+
# @param[String] :settings_file Settings File Content if already present
|
40
|
+
|
41
|
+
def initialize(
|
42
|
+
account_id,
|
43
|
+
sdk_key,
|
44
|
+
logger = nil,
|
45
|
+
user_profile_service = nil,
|
46
|
+
is_development_mode = false,
|
47
|
+
settings_file = nil
|
48
|
+
)
|
49
|
+
@account_id = account_id
|
50
|
+
@sdk_key = sdk_key
|
51
|
+
@user_profile_service = user_profile_service
|
52
|
+
@is_development_mode = is_development_mode
|
53
|
+
|
54
|
+
# Verify and assign a/the logger (Pending)
|
55
|
+
@logger = CustomLogger.get_instance(logger)
|
56
|
+
# Verify the settings_file for json object and correct schema
|
57
|
+
|
58
|
+
unless valid_settings_file?(get_settings(settings_file))
|
59
|
+
@logger.log(
|
60
|
+
LogLevelEnum::ERROR,
|
61
|
+
format(LogMessageEnum::ErrorMessages::SETTINGS_FILE_CORRUPTED, file: FILE)
|
62
|
+
)
|
63
|
+
@is_valid = false
|
64
|
+
return
|
65
|
+
end
|
66
|
+
@is_valid = true
|
67
|
+
|
68
|
+
# Initialize the ProjectConfigManager if settings_file provided is valid
|
69
|
+
@config = VWO::ProjectConfigManager.new(get_settings)
|
70
|
+
|
71
|
+
@logger.log(
|
72
|
+
LogLevelEnum::DEBUG,
|
73
|
+
format(LogMessageEnum::DebugMessages::VALID_CONFIGURATION, file: FILE)
|
74
|
+
)
|
75
|
+
|
76
|
+
# Process the settings file
|
77
|
+
@config.process_settings_file
|
78
|
+
@settings_file = @config.get_settings_file
|
79
|
+
|
80
|
+
# Assign DecisionService to vwo
|
81
|
+
@decision_service = DecisionService.new(@settings_file, user_profile_service)
|
82
|
+
|
83
|
+
# Assign event dispatcher
|
84
|
+
if is_development_mode
|
85
|
+
@logger.log(
|
86
|
+
LogLevelEnum::DEBUG,
|
87
|
+
format(LogMessageEnum::DebugMessages::SET_DEVELOPMENT_MODE, file: FILE)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
@event_dispatcher = EventDispatcher.new(is_development_mode)
|
91
|
+
|
92
|
+
# Log successfully initialized SDK
|
93
|
+
@logger.log(
|
94
|
+
LogLevelEnum::DEBUG,
|
95
|
+
format(LogMessageEnum::DebugMessages::SDK_INITIALIZED, file: FILE)
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
# VWO get_settings method to get settings for a particular account_id
|
100
|
+
# It will memoize the settings to avoid another http call on re-invocation of this method
|
101
|
+
def get_settings(settings_file = nil)
|
102
|
+
@settings ||=
|
103
|
+
settings_file || VWO::GetSettings.new(@account_id, @sdk_key).get
|
104
|
+
@settings
|
105
|
+
end
|
106
|
+
|
107
|
+
# This API method: Gets the variation assigned for the user
|
108
|
+
# For the campaign and send the metrics to VWO server
|
109
|
+
#
|
110
|
+
# 1. Validates the arguments being passed
|
111
|
+
# 2. Checks if user is eligible to get bucketed into the campaign,
|
112
|
+
# 3. Assigns the deterministic variation to the user(based on userId),
|
113
|
+
# If user becomes part of campaign
|
114
|
+
# If userProfileService is used, it will look into it for the
|
115
|
+
# Variation and if found, no further processing is done
|
116
|
+
# 4. Sends an impression call to VWO server to track user
|
117
|
+
#
|
118
|
+
# @param[String] :campaign_test_key Unique campaign test key
|
119
|
+
# @param[String] :user_id ID assigned to a user
|
120
|
+
# @return[String|None] If variation is assigned then variation-name
|
121
|
+
# otherwise None in case of user not becoming part
|
122
|
+
|
123
|
+
def activate(campaign_test_key, user_id)
|
124
|
+
# Validate input parameters
|
125
|
+
unless valid_string?(campaign_test_key) && valid_string?(user_id)
|
126
|
+
@logger.log(
|
127
|
+
LogLevelEnum::ERROR,
|
128
|
+
format(LogMessageEnum::ErrorMessages::ACTIVATE_API_MISSING_PARAMS, file: FILE)
|
129
|
+
)
|
130
|
+
return
|
131
|
+
end
|
132
|
+
|
133
|
+
# Validate project config manager
|
134
|
+
unless @is_valid
|
135
|
+
@logger.log(
|
136
|
+
LogLevelEnum::ERROR,
|
137
|
+
format(LogMessageEnum::ErrorMessages::ACTIVATE_API_CONFIG_CORRUPTED, file: FILE)
|
138
|
+
)
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
# Get the campaign settings
|
143
|
+
campaign = get_campaign(@settings_file, campaign_test_key)
|
144
|
+
|
145
|
+
# Validate campaign
|
146
|
+
unless campaign && campaign['status'] == STATUS_RUNNING
|
147
|
+
# Campaign is invalid
|
148
|
+
@logger.log(
|
149
|
+
LogLevelEnum::ERROR,
|
150
|
+
format(LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING, file: FILE, campaign_test_key: campaign_test_key, api: 'activate')
|
151
|
+
)
|
152
|
+
return
|
153
|
+
end
|
154
|
+
|
155
|
+
# Once the matching RUNNING campaign is found, assign the
|
156
|
+
# deterministic variation to the user_id provided
|
157
|
+
variation_id, variation_name = @decision_service.get(
|
158
|
+
user_id,
|
159
|
+
campaign,
|
160
|
+
campaign_test_key
|
161
|
+
)
|
162
|
+
|
163
|
+
# Check if variation_name has been assigned
|
164
|
+
unless valid_value?(variation_name)
|
165
|
+
# log invalid variation key
|
166
|
+
@logger.log(
|
167
|
+
LogLevelEnum::INFO,
|
168
|
+
format(LogMessageEnum::InfoMessages::INVALID_VARIATION_KEY, file: FILE, user_id: user_id, campaign_test_key: campaign_test_key)
|
169
|
+
)
|
170
|
+
return
|
171
|
+
end
|
172
|
+
|
173
|
+
# Variation found, dispatch log to DACDN
|
174
|
+
properties = build_event(
|
175
|
+
@settings_file,
|
176
|
+
campaign['id'],
|
177
|
+
variation_id,
|
178
|
+
user_id
|
179
|
+
)
|
180
|
+
@event_dispatcher.dispatch(properties)
|
181
|
+
variation_name
|
182
|
+
end
|
183
|
+
|
184
|
+
# This API method: Gets the variation assigned for the
|
185
|
+
# user for the campaign
|
186
|
+
#
|
187
|
+
# 1. Validates the arguments being passed
|
188
|
+
# 2. Checks if user is eligible to get bucketed into the campaign,
|
189
|
+
# 3. Assigns the deterministic variation to the user(based on user_id),
|
190
|
+
# If user becomes part of campaign
|
191
|
+
# If userProfileService is used, it will look into it for the
|
192
|
+
# variation and if found, no further processing is done
|
193
|
+
#
|
194
|
+
# @param[String] :campaign_test_key Unique campaign test key
|
195
|
+
# @param[String] :user_id ID assigned to a user
|
196
|
+
#
|
197
|
+
# @@return[String|Nil] If variation is assigned then variation-name
|
198
|
+
# Otherwise null in case of user not becoming part
|
199
|
+
#
|
200
|
+
def get_variation(campaign_test_key, user_id)
|
201
|
+
# Check for valid arguments
|
202
|
+
unless valid_string?(campaign_test_key) && valid_string?(user_id)
|
203
|
+
# log invalid params
|
204
|
+
@logger.log(
|
205
|
+
LogLevelEnum::ERROR,
|
206
|
+
format(LogMessageEnum::ErrorMessages::GET_VARIATION_API_MISSING_PARAMS, file: FILE)
|
207
|
+
)
|
208
|
+
return
|
209
|
+
end
|
210
|
+
|
211
|
+
# Validate project config manager
|
212
|
+
unless @is_valid
|
213
|
+
@logger.log(
|
214
|
+
LogLevelEnum::ERROR,
|
215
|
+
format(LogMessageEnum::ErrorMessages::ACTIVATE_API_CONFIG_CORRUPTED, file: FILE)
|
216
|
+
)
|
217
|
+
return
|
218
|
+
end
|
219
|
+
|
220
|
+
# Get the campaign settings
|
221
|
+
campaign = get_campaign(@settings_file, campaign_test_key)
|
222
|
+
|
223
|
+
# Validate campaign
|
224
|
+
if campaign.nil? || campaign['status'] != STATUS_RUNNING
|
225
|
+
# log campaigns invalid
|
226
|
+
@logger.log(
|
227
|
+
LogLevelEnum::ERROR,
|
228
|
+
format(LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING, file: FILE, campaign_test_key: campaign_test_key, api: 'get_variation')
|
229
|
+
)
|
230
|
+
return
|
231
|
+
end
|
232
|
+
|
233
|
+
_variation_id, variation_name = @decision_service.get(
|
234
|
+
user_id,
|
235
|
+
campaign,
|
236
|
+
campaign_test_key
|
237
|
+
)
|
238
|
+
|
239
|
+
# Check if variation_name has been assigned
|
240
|
+
unless valid_value?(variation_name)
|
241
|
+
# log invalid variation key
|
242
|
+
@logger.log(
|
243
|
+
LogLevelEnum::INFO,
|
244
|
+
format(LogMessageEnum::InfoMessages::INVALID_VARIATION_KEY, file: FILE, user_id: user_id, campaign_test_key: campaign_test_key)
|
245
|
+
)
|
246
|
+
return
|
247
|
+
end
|
248
|
+
|
249
|
+
variation_name
|
250
|
+
end
|
251
|
+
|
252
|
+
# This API method: Marks the conversion of the campaign
|
253
|
+
# for a particular goal
|
254
|
+
# 1. validates the arguments being passed
|
255
|
+
# 2. Checks if user is eligible to get bucketed into the campaign,
|
256
|
+
# 3. Gets the assigned deterministic variation to the
|
257
|
+
# user(based on user_d), if user becomes part of campaign
|
258
|
+
# 4. Sends an impression call to VWO server to track goal data
|
259
|
+
#
|
260
|
+
# @param[String] :campaign_test_key Unique campaign test key
|
261
|
+
# @param[String] :user_id ID assigned to a user
|
262
|
+
# @param[String] :goal_identifier Unique campaign's goal identifier
|
263
|
+
# @param[Numeric|String] :revenue_value Revenue generated on triggering the goal
|
264
|
+
#
|
265
|
+
def track(campaign_test_key, user_id, goal_identifier, *args)
|
266
|
+
if args.is_a?(Array)
|
267
|
+
revenue_value = args[0]
|
268
|
+
elsif args.is_a?(Hash)
|
269
|
+
revenue_value = args['revenue_value']
|
270
|
+
end
|
271
|
+
|
272
|
+
# Check for valid args
|
273
|
+
unless valid_string?(campaign_test_key) && valid_string?(user_id) && valid_string?(goal_identifier)
|
274
|
+
# log invalid params
|
275
|
+
@logger.log(
|
276
|
+
LogLevelEnum::ERROR,
|
277
|
+
format(LogMessageEnum::ErrorMessages::TRACK_API_MISSING_PARAMS, file: FILE)
|
278
|
+
)
|
279
|
+
return false
|
280
|
+
end
|
281
|
+
|
282
|
+
unless @is_valid
|
283
|
+
@logger.log(
|
284
|
+
LogLevelEnum::ERROR,
|
285
|
+
format(LogMessageEnum::ErrorMessages::ACTIVATE_API_CONFIG_CORRUPTED, file: FILE)
|
286
|
+
)
|
287
|
+
return false
|
288
|
+
end
|
289
|
+
|
290
|
+
# Get the campaign settings
|
291
|
+
campaign = get_campaign(@settings_file, campaign_test_key)
|
292
|
+
|
293
|
+
# Validate campaign
|
294
|
+
if campaign.nil? || campaign['status'] != STATUS_RUNNING
|
295
|
+
# log error
|
296
|
+
@logger.log(
|
297
|
+
LogLevelEnum::ERROR,
|
298
|
+
format(LogMessageEnum::ErrorMessages::CAMPAIGN_NOT_RUNNING, file: FILE, campaign_test_key: campaign_test_key, api: 'track')
|
299
|
+
)
|
300
|
+
return false
|
301
|
+
end
|
302
|
+
|
303
|
+
campaign_id = campaign['id']
|
304
|
+
variation_id, variation_name = @decision_service.get_variation_allotted(user_id, campaign)
|
305
|
+
|
306
|
+
if variation_name
|
307
|
+
goal = get_campaign_goal(@settings_file, campaign['key'], goal_identifier)
|
308
|
+
|
309
|
+
if goal.nil?
|
310
|
+
@logger.log(
|
311
|
+
LogLevelEnum::ERROR,
|
312
|
+
format(
|
313
|
+
LogMessageEnum::ErrorMessages::TRACK_API_GOAL_NOT_FOUND,
|
314
|
+
file: FILE, goal_identifier: goal_identifier,
|
315
|
+
user_id: user_id,
|
316
|
+
campaign_test_key: campaign_test_key
|
317
|
+
)
|
318
|
+
)
|
319
|
+
return false
|
320
|
+
elsif goal['type'] == GOALTYPES::REVENUE && !valid_value?(revenue_value)
|
321
|
+
@logger.log(
|
322
|
+
LogLevelEnum::ERROR,
|
323
|
+
format(
|
324
|
+
LogMessageEnum::ErrorMessages::TRACK_API_REVENUE_NOT_PASSED_FOR_REVENUE_GOAL,
|
325
|
+
file: FILE,
|
326
|
+
user_id: user_id,
|
327
|
+
goal_identifier: goal_identifier,
|
328
|
+
campaign_test_key: campaign_test_key
|
329
|
+
)
|
330
|
+
)
|
331
|
+
return false
|
332
|
+
end
|
333
|
+
|
334
|
+
revenue_value = nil if goal['type'] == GOALTYPES::CUSTOM
|
335
|
+
|
336
|
+
properties = build_event(
|
337
|
+
@settings_file,
|
338
|
+
campaign_id,
|
339
|
+
variation_id,
|
340
|
+
user_id,
|
341
|
+
goal['id'],
|
342
|
+
revenue_value
|
343
|
+
)
|
344
|
+
@event_dispatcher.dispatch(properties)
|
345
|
+
return true
|
346
|
+
end
|
347
|
+
false
|
348
|
+
end
|
349
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vwo-ruby-sdk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- VWO
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-09-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: coveralls
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.23
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.23
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.70'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.70'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json-schema
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: murmurhash3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.1'
|
69
|
+
description: A Ruby SDK for VWO full-stack testing.
|
70
|
+
email:
|
71
|
+
- dev@wingify.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/vwo.rb
|
77
|
+
- lib/vwo/bucketing_service.rb
|
78
|
+
- lib/vwo/common/campaign_utils.rb
|
79
|
+
- lib/vwo/common/constants.rb
|
80
|
+
- lib/vwo/common/enums.rb
|
81
|
+
- lib/vwo/common/function_utils.rb
|
82
|
+
- lib/vwo/common/impression_utils.rb
|
83
|
+
- lib/vwo/common/requests.rb
|
84
|
+
- lib/vwo/common/schemas/settings_file.rb
|
85
|
+
- lib/vwo/common/utils.rb
|
86
|
+
- lib/vwo/common/uuid_utils.rb
|
87
|
+
- lib/vwo/common/validations.rb
|
88
|
+
- lib/vwo/custom_logger.rb
|
89
|
+
- lib/vwo/decision_service.rb
|
90
|
+
- lib/vwo/event_dispatcher.rb
|
91
|
+
- lib/vwo/get_settings.rb
|
92
|
+
- lib/vwo/project_config_manager.rb
|
93
|
+
- lib/vwo/user_profile.rb
|
94
|
+
homepage: https://vwo.com/fullstack/server-side-testing/
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.5.2.3
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Ruby SDK for VWO full-stack testing
|
118
|
+
test_files: []
|