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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bb6f642d7cd1669a15bc5a0d2f269a83e92c9d31
|
4
|
+
data.tar.gz: 299909d28f2c4ea3b6e2bac8eb27670dfba39ec9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b16d5035c15fb7e292e6c4ab116180e652a9a852d10d9e435facf9b85f290e2063a6d05114cb0e215149c0cd98f68281e7dea1ba8394508cf4380d31fbfd86ba
|
7
|
+
data.tar.gz: 23c28fac8fdee1ab542eb8d7930a222889f4e6542bdc3071f0baf1531febb77c39f2c2598de9a68a152f1fcfcbce2bb4050b088c46a19624928ed6dedc5745f4
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'murmurhash3'
|
4
|
+
require_relative 'custom_logger'
|
5
|
+
require_relative 'common/enums'
|
6
|
+
require_relative 'common/validations'
|
7
|
+
require_relative 'common/constants'
|
8
|
+
|
9
|
+
# Class encapsulating all decision related capabilities.
|
10
|
+
class VWO
|
11
|
+
class BucketingService
|
12
|
+
include VWO::Common::Enums
|
13
|
+
include VWO::Common::CONSTANTS
|
14
|
+
include VWO::Common::Validations
|
15
|
+
|
16
|
+
U_MAX_32_BIT = 0xFFFFFFFF
|
17
|
+
MAX_HASH_VALUE = 2**32
|
18
|
+
FILE = FileNameEnum::BucketingService
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@logger = CustomLogger.get_instance
|
22
|
+
end
|
23
|
+
|
24
|
+
# Calculate if this user should become part of the campaign or not
|
25
|
+
# @param[String] :user_id The unique ID assigned to a user
|
26
|
+
# @param[Dict] :campaign For getting traffic allotted to the campaign
|
27
|
+
# @return[Boolean] If User is a part of Campaign or not
|
28
|
+
#
|
29
|
+
def user_part_of_campaign?(user_id, campaign)
|
30
|
+
unless valid_value?(user_id)
|
31
|
+
@logger.log(
|
32
|
+
LogLevelEnum::ERROR,
|
33
|
+
format(LogMessageEnum::ErrorMessages::INVALID_USER_ID, file: FILE, user_id: user_id, method: 'is_user_part_of_campaign')
|
34
|
+
)
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
if campaign.nil?
|
39
|
+
@logger.log(
|
40
|
+
LogLevelEnum::ERROR,
|
41
|
+
format(LogMessageEnum::ErrorMessages::INVALID_CAMPAIGN, file: FILE, method: 'is_user_part_of_campaign')
|
42
|
+
)
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
traffic_allocation = campaign['percentTraffic']
|
47
|
+
|
48
|
+
value_assigned_to_user = get_bucket_value_for_user(user_id)
|
49
|
+
is_user_part = (value_assigned_to_user != 0) && value_assigned_to_user <= traffic_allocation
|
50
|
+
@logger.log(
|
51
|
+
LogLevelEnum::INFO,
|
52
|
+
format(LogMessageEnum::InfoMessages::USER_ELIGIBILITY_FOR_CAMPAIGN, file: FILE, user_id: user_id, is_user_part: is_user_part)
|
53
|
+
)
|
54
|
+
is_user_part
|
55
|
+
end
|
56
|
+
|
57
|
+
# Validates the User ID and
|
58
|
+
# Generates Variation into which the User is bucketed in.
|
59
|
+
#
|
60
|
+
# @param[String] :user_id The unique ID assigned to User
|
61
|
+
# @param[Hash] :campaign The Campaign of which User is a part of
|
62
|
+
#
|
63
|
+
# @return[Hash|nil} Variation data into which user is bucketed in
|
64
|
+
# or nil if not
|
65
|
+
def bucket_user_to_variation(user_id, campaign)
|
66
|
+
unless valid_value?(user_id)
|
67
|
+
@logger.log(
|
68
|
+
LogLevelEnum::ERROR,
|
69
|
+
format(LogMessageEnum::ErrorMessages::INVALID_USER_ID, file: FILE, user_id: user_id, method: 'bucket_user_to_variation')
|
70
|
+
)
|
71
|
+
return
|
72
|
+
end
|
73
|
+
|
74
|
+
unless campaign
|
75
|
+
@logger.log(
|
76
|
+
LogLevelEnum::ERROR,
|
77
|
+
format(LogMessageEnum::ErrorMessages::INVALID_CAMPAIGN, file: FILE, method: 'is_user_part_of_campaign')
|
78
|
+
)
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
hash_value = MurmurHash3::V32.str_hash(user_id, SEED_VALUE) & U_MAX_32_BIT
|
83
|
+
normalize = MAX_TRAFFIC_VALUE / campaign['percentTraffic']
|
84
|
+
multiplier = normalize / 100
|
85
|
+
bucket_value = generate_bucket_value(
|
86
|
+
hash_value,
|
87
|
+
MAX_TRAFFIC_VALUE,
|
88
|
+
multiplier
|
89
|
+
)
|
90
|
+
|
91
|
+
@logger.log(
|
92
|
+
LogLevelEnum::DEBUG,
|
93
|
+
format(
|
94
|
+
LogMessageEnum::DebugMessages::VARIATION_HASH_BUCKET_VALUE,
|
95
|
+
file: FILE,
|
96
|
+
user_id: user_id,
|
97
|
+
campaign_test_key: campaign['key'],
|
98
|
+
percent_traffic: campaign['percentTraffic'],
|
99
|
+
bucket_value: bucket_value,
|
100
|
+
hash_value: hash_value
|
101
|
+
)
|
102
|
+
)
|
103
|
+
get_variation(campaign, bucket_value)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Returns the Variation by checking the Start and End
|
109
|
+
# Bucket Allocations of each Variation
|
110
|
+
#
|
111
|
+
# @param[Hash] :campaign Which contains the variations
|
112
|
+
# @param[Integer] :bucket_value The bucket Value of the user
|
113
|
+
# @return[Hash|nil] Variation data allotted to the user or None if not
|
114
|
+
#
|
115
|
+
def get_variation(campaign, bucket_value)
|
116
|
+
campaign['variations'].find do |variation|
|
117
|
+
(variation['start_variation_allocation']..variation['end_variation_allocation']).cover?(bucket_value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Validates the User ID and generates Bucket Value of the
|
122
|
+
# User by hashing the userId by murmurHash and scaling it down.
|
123
|
+
#
|
124
|
+
# @param[String] :user_id The unique ID assigned to User
|
125
|
+
# @return[Integer] The bucket Value allotted to User
|
126
|
+
# (between 1 to $this->$MAX_TRAFFIC_PERCENT)
|
127
|
+
def get_bucket_value_for_user(user_id)
|
128
|
+
hash_value = MurmurHash3::V32.str_hash(user_id, SEED_VALUE) & U_MAX_32_BIT
|
129
|
+
bucket_value = generate_bucket_value(hash_value, MAX_TRAFFIC_PERCENT)
|
130
|
+
|
131
|
+
@logger.log(
|
132
|
+
LogLevelEnum::DEBUG,
|
133
|
+
format(
|
134
|
+
LogMessageEnum::DebugMessages::USER_HASH_BUCKET_VALUE,
|
135
|
+
file: FILE,
|
136
|
+
hash_value: hash_value,
|
137
|
+
bucket_value: bucket_value,
|
138
|
+
user_id: user_id
|
139
|
+
)
|
140
|
+
)
|
141
|
+
bucket_value
|
142
|
+
end
|
143
|
+
|
144
|
+
# Generates Bucket Value of the User by hashing the User ID by murmurHash
|
145
|
+
# And scaling it down.
|
146
|
+
#
|
147
|
+
# @param[Integer] :hash_value HashValue generated after hashing
|
148
|
+
# @param[Integer] :max_value The value up-to which hashValue needs to be scaled
|
149
|
+
# @param[Integer] :multiplier
|
150
|
+
# @return[Integer] Bucket Value of the User
|
151
|
+
#
|
152
|
+
def generate_bucket_value(hash_value, max_value, multiplier = 1)
|
153
|
+
ratio = hash_value.to_f / MAX_HASH_VALUE
|
154
|
+
multiplied_value = (max_value * ratio + 1) * multiplier
|
155
|
+
multiplied_value.to_i
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../custom_logger'
|
4
|
+
require_relative 'enums'
|
5
|
+
require_relative 'constants'
|
6
|
+
|
7
|
+
# Utility module for manipulating VWO campaigns
|
8
|
+
class VWO
|
9
|
+
module Common
|
10
|
+
module CampaignUtils
|
11
|
+
include VWO::Common::Enums
|
12
|
+
include VWO::Common::CONSTANTS
|
13
|
+
|
14
|
+
# Finds and Returns campaign from given campaign_test_key.
|
15
|
+
#
|
16
|
+
# @param[Hash] :settings_file Settings file for the project
|
17
|
+
# @param[String] :campaign_test_key Campaign identifier key
|
18
|
+
# @return[Hash] :campaign object
|
19
|
+
|
20
|
+
def get_campaign(settings_file, campaign_test_key)
|
21
|
+
(settings_file['campaigns'] || []).find do |campaign|
|
22
|
+
campaign['key'] == campaign_test_key
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets variation allocation range in the provided campaign
|
27
|
+
#
|
28
|
+
# @param [Hash]: Campaign object
|
29
|
+
|
30
|
+
def set_variation_allocation(campaign)
|
31
|
+
current_allocation = 0
|
32
|
+
campaign['variations'].each do |variation|
|
33
|
+
step_factor = get_variation_bucketing_range(variation['weight'])
|
34
|
+
if step_factor
|
35
|
+
start_range = current_allocation + 1
|
36
|
+
end_range = current_allocation + step_factor
|
37
|
+
variation['start_variation_allocation'] = start_range
|
38
|
+
variation['end_variation_allocation'] = end_range
|
39
|
+
current_allocation += step_factor
|
40
|
+
else
|
41
|
+
variation['start_variation_allocation'] = -1
|
42
|
+
variation['end_variation_allocation'] = -1
|
43
|
+
end
|
44
|
+
|
45
|
+
CustomLogger.get_instance.log(
|
46
|
+
LogLevelEnum::INFO,
|
47
|
+
format(
|
48
|
+
LogMessageEnum::InfoMessages::VARIATION_RANGE_ALLOCATION,
|
49
|
+
file: FileNameEnum::CampaignUtil,
|
50
|
+
campaign_test_key: campaign['key'],
|
51
|
+
variation_name: variation['name'],
|
52
|
+
variation_weight: variation['weight'],
|
53
|
+
start: variation['start_variation_allocation'],
|
54
|
+
end: variation['end_variation_allocation']
|
55
|
+
)
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns goal from given campaign_test_key and gaol_identifier.
|
61
|
+
# @param[Hash] :settings_file Settings file of the project
|
62
|
+
# @param[String] :campaign_test_key Campaign identifier key
|
63
|
+
# @param[String] :goal_identifier Goal identifier
|
64
|
+
#
|
65
|
+
# @return[Hash] Goal corresponding to Goal_identifier in respective campaign
|
66
|
+
|
67
|
+
def get_campaign_goal(settings_file, campaign_test_key, goal_identifier)
|
68
|
+
return unless settings_file && campaign_test_key && goal_identifier
|
69
|
+
|
70
|
+
campaign = get_campaign(settings_file, campaign_test_key)
|
71
|
+
return unless campaign
|
72
|
+
|
73
|
+
campaign['goals'].find do |goal|
|
74
|
+
goal['identifier'] == goal_identifier
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Returns the bucket size of variation.
|
81
|
+
# @param (Number): weight of variation
|
82
|
+
# @return (Integer): Bucket start range of Variation
|
83
|
+
|
84
|
+
def get_variation_bucketing_range(weight)
|
85
|
+
return 0 if weight.nil? || weight == 0
|
86
|
+
|
87
|
+
start_range = (weight * 100).ceil.to_i
|
88
|
+
[start_range, MAX_TRAFFIC_VALUE].min
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns variation from given campaign_test_key and variation_name.
|
92
|
+
#
|
93
|
+
# @param[Hash] :settings_file Settings file of the project
|
94
|
+
# @param[Hash] :campaign_test_key Campaign identifier key
|
95
|
+
# @param[String] :variation_name Variation identifier
|
96
|
+
#
|
97
|
+
# @return[Hash] Variation corresponding to variation_name in respective campaign
|
98
|
+
|
99
|
+
def get_campaign_variation(settings_file, campaign_test_key, variation_name)
|
100
|
+
return if settings_file && campaign_test_key && variation_name
|
101
|
+
|
102
|
+
campaign = get_campaign(settings_file, campaign_test_key)
|
103
|
+
return unless campaign
|
104
|
+
|
105
|
+
campaign['variations'].find do |variation|
|
106
|
+
variation['name'] == variation_name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VWO
|
4
|
+
module Common
|
5
|
+
module CONSTANTS
|
6
|
+
API_VERSION = 2
|
7
|
+
PLATFORM = 'server'
|
8
|
+
SEED_VALUE = 1
|
9
|
+
MAX_TRAFFIC_PERCENT = 100
|
10
|
+
MAX_TRAFFIC_VALUE = 10_000
|
11
|
+
STATUS_RUNNING = 'RUNNING'
|
12
|
+
LIBRARY_PATH = File.expand_path('../..', __FILE__)
|
13
|
+
HTTP_PROTOCOL = 'http://'
|
14
|
+
HTTPS_PROTOCOL = 'https://'
|
15
|
+
URL_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'
|
16
|
+
SDK_VERSION = '1.0.0'
|
17
|
+
|
18
|
+
module ENDPOINTS
|
19
|
+
BASE_URL = 'dev.visualwebsiteoptimizer.com'
|
20
|
+
ACCOUNT_SETTINGS = '/server-side/settings'
|
21
|
+
TRACK_USER = '/server-side/track-user'
|
22
|
+
TRACK_GOAL = '/server-side/track-goal'
|
23
|
+
end
|
24
|
+
|
25
|
+
module EVENTS
|
26
|
+
TRACK_USER = 'track-user'
|
27
|
+
TRACK_GOAL = 'track-goal'
|
28
|
+
end
|
29
|
+
|
30
|
+
module DATATYPE
|
31
|
+
NUMBER = 'number'
|
32
|
+
STRING = 'string'
|
33
|
+
FUNCTION = 'function'
|
34
|
+
BOOLEAN = 'boolean'
|
35
|
+
end
|
36
|
+
|
37
|
+
module APIMETHODS
|
38
|
+
CREATE_INSTANCE = 'CREATE_INSTANCE'
|
39
|
+
ACTIVATE = 'ACTIVATE'
|
40
|
+
GET_VARIATION = 'GET_VARIATION'
|
41
|
+
TRACK = 'TRACK'
|
42
|
+
end
|
43
|
+
|
44
|
+
module GOALTYPES
|
45
|
+
REVENUE = 'REVENUE_TRACKING'
|
46
|
+
CUSTOM = 'CUSTOM_GOAL'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/LineLength
|
4
|
+
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
class VWO
|
8
|
+
module Common
|
9
|
+
module Enums
|
10
|
+
module FileNameEnum
|
11
|
+
VWO_PATH = 'vwo'
|
12
|
+
COMMON_PATH = 'vwo/common'
|
13
|
+
|
14
|
+
VWO = VWO_PATH + '/vwo'
|
15
|
+
BucketingService = VWO_PATH + '/bucketing_service'
|
16
|
+
DecisionService = VWO_PATH + '/decision_service'
|
17
|
+
EventDispatcher = VWO_PATH + '/event_dispatcher'
|
18
|
+
Logger = VWO_PATH + '/logger'
|
19
|
+
ProjectConfigManager = VWO_PATH + '/project_config_manager'
|
20
|
+
|
21
|
+
CampaignUtil = COMMON_PATH + '/campaign_util'
|
22
|
+
FunctionUtil = COMMON_PATH + '/function_util'
|
23
|
+
ImpressionUtil = COMMON_PATH + '/impression_util'
|
24
|
+
UuidUtil = COMMON_PATH + '/uuid_util'
|
25
|
+
ValidateUtil = COMMON_PATH + '/validate_util'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Classobj encapsulating various logging messages
|
29
|
+
module LogMessageEnum
|
30
|
+
# Classobj encapsulating various DEBUG messages
|
31
|
+
module DebugMessages
|
32
|
+
LOG_LEVEL_SET = '(%<file>s): Log level set to %<level>s'
|
33
|
+
SET_COLORED_LOG = '(%<file>s): Colored log set to %<value>s'
|
34
|
+
SET_DEVELOPMENT_MODE = '(%<file>s): DEVELOPMENT mode is ON'
|
35
|
+
VALID_CONFIGURATION = '(%<file>s): SDK configuration and account settings are valid.'
|
36
|
+
CUSTOM_LOGGER_USED = '(%<file>s): Custom logger used'
|
37
|
+
SDK_INITIALIZED = '(%<file>s): SDK properly initialized'
|
38
|
+
SETTINGS_FILE_PROCESSED = '(%<file>s): Settings file processed'
|
39
|
+
NO_STORED_VARIATION = '(%<file>s): No stored variation for UserId:%<user_id>s for Campaign:%<campaign_test_key>s found in UserProfileService'
|
40
|
+
NO_USER_PROFILE_SERVICE_LOOKUP = '(%<file>s): No UserProfileService to look for stored data'
|
41
|
+
NO_USER_PROFILE_SERVICE_SAVE = '(%<file>s): No UserProfileService to save data'
|
42
|
+
GETTING_STORED_VARIATION = '(%<file>s): Got stored variation for UserId:%<user_id>s of Campaign:%<campaign_test_key>s as Variation: %<variation_name>s found in UserProfileService'
|
43
|
+
CHECK_USER_ELIGIBILITY_FOR_CAMPAIGN = '(%<file>s): campaign:%<campaign_test_key>s having traffic allocation:%<traffic_allocation>s assigned value:%<traffic_allocation>s to userId:%<user_id>s'
|
44
|
+
USER_HASH_BUCKET_VALUE = '(%<file>s): userId:%<user_id>s having hash:%<hash_value>s got bucketValue:%<bucket_value>s'
|
45
|
+
VARIATION_HASH_BUCKET_VALUE = '(%<file>s): userId:%<user_id>s for campaign:%<campaign_test_key>s having percent traffic:%<percent_traffic>s got hash-value:%<hash_value>s and bucket value:%<bucket_value>s'
|
46
|
+
IMPRESSION_FAILED = '(%<file>s): userId:%<user_id>s for campaign:%<campaign_test_key>s got variationName:%<variation_name>s inside method:%<method>s'
|
47
|
+
USER_NOT_PART_OF_CAMPAIGN = '(%<file>s): userId:%<user_id>s for campaign:%<campaign_test_key>s did not become part of campaign method:%<method>s'
|
48
|
+
UUID_FOR_USER = '(%<file>s): Uuid generated for userId:%<user_id>s and accountId:%<account_id>s is %<desired_uuid>s'
|
49
|
+
IMPRESSION_FOR_TRACK_USER = '(%<file>s): Impression built for track-user - %<properties>s'
|
50
|
+
IMPRESSION_FOR_TRACK_GOAL = '(%<file>s): Impression built for track-goal - %<properties>s'
|
51
|
+
GOT_VARIATION_FOR_USER = '(%<file>s): userId:%<user_id>s for campaign:%<campaign_test_key>s got variationName:%<variation_name>s'
|
52
|
+
end
|
53
|
+
|
54
|
+
# Classobj encapsulating various INFO messages
|
55
|
+
module InfoMessages
|
56
|
+
VARIATION_RANGE_ALLOCATION = '(%<file>s): Campaign:%<campaign_test_key>s having variations:%<variation_name>s with weight:%<variation_weight>s got range as: ( %<start>s - %<end>s ))'
|
57
|
+
VARIATION_ALLOCATED = '(%<file>s): UserId:%<user_id>s of Campaign:%<campaign_test_key>s got variation: %<variation_name>s'
|
58
|
+
LOOKING_UP_USER_PROFILE_SERVICE = '(%<file>s): Looked into UserProfileService for userId:%<user_id>s %<status>s'
|
59
|
+
SAVING_DATA_USER_PROFILE_SERVICE = '(%<file>s): Saving into UserProfileService for userId:%<user_id>s successful'
|
60
|
+
GOT_STORED_VARIATION = '(%<file>s): Got stored variation:%<variation_name>s of campaign:%<campaign_test_key>s for userId:%<user_id>s from UserProfileService'
|
61
|
+
NO_VARIATION_ALLOCATED = '(%<file>s): UserId:%<user_id>s of Campaign:%<campaign_test_key>s did not get any variation'
|
62
|
+
USER_ELIGIBILITY_FOR_CAMPAIGN = '(%<file>s): Is userId:%<user_id>s part of campaign? %<is_user_part>s'
|
63
|
+
AUDIENCE_CONDITION_NOT_MET = '(%<file>s): userId:%<user_id>s does not become part of campaign because of not meeting audience conditions'
|
64
|
+
GOT_VARIATION_FOR_USER = '(%<file>s): userId:%<user_id>s for campaign:%<campaign_test_key>s got variationName:%<variation_name>s'
|
65
|
+
USER_GOT_NO_VARIATION = '(%<file>s): userId:%<user_id>s for campaign:%<campaign_test_key>s did not allot any variation'
|
66
|
+
IMPRESSION_SUCCESS = '(%<file>s): Impression event - %<end_point>s was successfully received by VWO having main keys: accountId:%<account_id>s userId:%<user_id>s campaignId:%<campaign_id>s and variationId:%<variation_id>s'
|
67
|
+
INVALID_VARIATION_KEY = '(%<file>s): Variation was not assigned to userId:%<user_id>s for campaign:%<campaign_test_key>s'
|
68
|
+
RETRY_FAILED_IMPRESSION_AFTER_DELAY = '(%<file>s): Failed impression event for %<end_point>s will be retried after %<retry_timeout>s milliseconds delay'
|
69
|
+
end
|
70
|
+
|
71
|
+
# Classobj encapsulating various WARNING messages
|
72
|
+
module WarningMessages; end
|
73
|
+
|
74
|
+
# Classobj encapsulating various ERROR messages
|
75
|
+
module ErrorMessages
|
76
|
+
PROJECT_CONFIG_CORRUPTED = '(%<file>s): config passed to createInstance is not a valid JSON object.'
|
77
|
+
INVALID_CONFIGURATION = '(%<file>s): SDK configuration or account settings or both is/are not valid.'
|
78
|
+
SETTINGS_FILE_CORRUPTED = '(%<file>s): Settings file is corrupted. Please contact VWO Support for help.'
|
79
|
+
ACTIVATE_API_MISSING_PARAMS = '(%<file>s): "activate" API got bad parameters. It expects campaignTestKey(String) as first and userId(String) as second argument'
|
80
|
+
ACTIVATE_API_CONFIG_CORRUPTED = '(%<file>s): "activate" API has corrupted configuration'
|
81
|
+
GET_VARIATION_API_MISSING_PARAMS = '(%<file>s): "getVariation" API got bad parameters. It expects campaignTestKey(String) as first and userId(String) as second argument'
|
82
|
+
GET_VARIATION_API_CONFIG_CORRUPTED = '(%<file>s): "getVariation" API has corrupted configuration'
|
83
|
+
TRACK_API_MISSING_PARAMS = '(%<file>s): "track" API got bad parameters. It expects campaignTestKey(String) as first userId(String) as second and goalIdentifier(String/Number) as third argument. Fourth is revenueValue(Float/Number/String) and is required for revenue goal only.'
|
84
|
+
TRACK_API_CONFIG_CORRUPTED = '(%<file>s): "track" API has corrupted configuration'
|
85
|
+
TRACK_API_GOAL_NOT_FOUND = '(%<file>s): Goal:%<goal_identifier>s not found for campaign:%<campaign_test_key>s and userId:%<user_id>s'
|
86
|
+
TRACK_API_REVENUE_NOT_PASSED_FOR_REVENUE_GOAL = '(%<file>s): Revenue value should be passed for revenue goal:%<goal_identifier>s for campaign:%<campaign_test_key>s and userId:%<user_id>s'
|
87
|
+
TRACK_API_VARIATION_NOT_FOUND = '(%<file>s): Variation not found for campaign:%<campaign_test_key>s and userId:%<user_id>s'
|
88
|
+
CAMPAIGN_NOT_RUNNING = '(%<file>s): API used:%<api>s - Campaign:%<campaign_test_key>s is not RUNNING. Please verify from VWO App'
|
89
|
+
LOOK_UP_USER_PROFILE_SERVICE_FAILED = '(%<file>s): Looking data from UserProfileService failed for userId:%<user_id>s'
|
90
|
+
SAVE_USER_PROFILE_SERVICE_FAILED = '(%<file>s): Saving data into UserProfileService failed for userId:%<user_id>s'
|
91
|
+
INVALID_CAMPAIGN = '(%<file>s): Invalid campaign passed to %<method>s of this file'
|
92
|
+
INVALID_USER_ID = '(%<file>s): Invalid userId:%<user_id>s passed to %<method>s of this file'
|
93
|
+
IMPRESSION_FAILED = '(%<file>s): Impression event could not be sent to VWO - %<end_point>s'
|
94
|
+
CUSTOM_LOGGER_MISCONFIGURED = '(%<file>s): Custom logger is provided but seems to have mis-configured. %<extra_info>s Please check the API Docs. Using default logger.'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
module LogLevelEnum
|
99
|
+
INFO = Logger::INFO
|
100
|
+
DEBUG = Logger::DEBUG
|
101
|
+
WARNING = Logger::WARN
|
102
|
+
ERROR = Logger::ERROR
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
# rubocop:enable Metrics/LineLength
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../custom_logger'
|
4
|
+
require_relative 'enums'
|
5
|
+
require_relative 'constants'
|
6
|
+
|
7
|
+
# Utility module for manipulating VWO campaigns
|
8
|
+
class VWO
|
9
|
+
module Common
|
10
|
+
module FunctionUtils
|
11
|
+
include VWO::Common::Enums
|
12
|
+
include VWO::Common::CONSTANTS
|
13
|
+
|
14
|
+
# @return[Float]
|
15
|
+
def get_random_number
|
16
|
+
rand
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return[Integer]
|
20
|
+
def get_current_unix_timestamp
|
21
|
+
Time.now.to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'cgi'
|
5
|
+
require_relative '../custom_logger'
|
6
|
+
require_relative 'enums'
|
7
|
+
require_relative 'constants'
|
8
|
+
require_relative 'function_utils'
|
9
|
+
require_relative 'uuid_utils'
|
10
|
+
|
11
|
+
# Utility module for manipulating VWO campaigns
|
12
|
+
class VWO
|
13
|
+
module Common
|
14
|
+
module ImpressionUtils
|
15
|
+
include VWO::Common::Enums
|
16
|
+
include VWO::Common::CONSTANTS
|
17
|
+
include FunctionUtils
|
18
|
+
include UUIDUtils
|
19
|
+
|
20
|
+
# Trigger the goal by sending it to server
|
21
|
+
#
|
22
|
+
# @param[Hash] :settings_file Settings file object
|
23
|
+
# @param[String] :campaign_id Campaign identifier
|
24
|
+
# @param[String] :variation_id Variation identifier
|
25
|
+
# @param[String] :user_id User identifier
|
26
|
+
# @param[String] :goal_id Goal identifier, if building track impression
|
27
|
+
# @param[String|Float|Integer|nil) :revenue Number value, in any representation, if building track impression
|
28
|
+
#
|
29
|
+
# @return[nil|Hash] None if campaign ID or variation ID is invalid,
|
30
|
+
# Else Properties(dict)
|
31
|
+
def build_event(settings_file, campaign_id, variation_id, user_id, goal_id = nil, revenue = nil)
|
32
|
+
return unless valid_number?(campaign_id) && valid_string?(user_id)
|
33
|
+
|
34
|
+
is_track_user_api = true
|
35
|
+
is_track_user_api = false unless goal_id.nil?
|
36
|
+
account_id = settings_file['accountId']
|
37
|
+
|
38
|
+
properties = {
|
39
|
+
account_id: account_id,
|
40
|
+
experiment_id: campaign_id,
|
41
|
+
ap: PLATFORM,
|
42
|
+
uId: CGI.escape(user_id.encode('utf-8')),
|
43
|
+
combination: variation_id,
|
44
|
+
random: get_random_number,
|
45
|
+
sId: get_current_unix_timestamp,
|
46
|
+
u: generator_for(user_id, account_id)
|
47
|
+
}
|
48
|
+
# Version and SDK constants
|
49
|
+
sdk_version = Gem.loaded_specs['vwo_sdk'] ? Gem.loaded_specs['vwo_sdk'].version : VWO::SDK_VERSION
|
50
|
+
properties['sdk'] = 'ruby'
|
51
|
+
properties['sdk-v'] = sdk_version
|
52
|
+
|
53
|
+
url = HTTPS_PROTOCOL + ENDPOINTS::BASE_URL
|
54
|
+
logger = VWO::CustomLogger.get_instance
|
55
|
+
|
56
|
+
if is_track_user_api
|
57
|
+
properties['ed'] = JSON.generate(p: 'server')
|
58
|
+
properties['url'] = "#{url}#{ENDPOINTS::TRACK_USER}"
|
59
|
+
logger.log(
|
60
|
+
LogLevelEnum::DEBUG,
|
61
|
+
format(
|
62
|
+
LogMessageEnum::DebugMessages::IMPRESSION_FOR_TRACK_USER,
|
63
|
+
file: FileNameEnum::ImpressionUtil,
|
64
|
+
properties: JSON.generate(properties)
|
65
|
+
)
|
66
|
+
)
|
67
|
+
else
|
68
|
+
properties['url'] = url + ENDPOINTS::TRACK_GOAL
|
69
|
+
properties['goal_id'] = goal_id
|
70
|
+
properties['r'] = revenue if revenue
|
71
|
+
logger.log(
|
72
|
+
LogLevelEnum::DEBUG,
|
73
|
+
format(
|
74
|
+
LogMessageEnum::DebugMessages::IMPRESSION_FOR_TRACK_GOAL,
|
75
|
+
file: FileNameEnum::ImpressionUtil,
|
76
|
+
properties: JSON.generate(properties)
|
77
|
+
)
|
78
|
+
)
|
79
|
+
end
|
80
|
+
properties
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
class VWO
|
6
|
+
module Common
|
7
|
+
class Requests
|
8
|
+
def self.get(url, params)
|
9
|
+
uri = URI.parse(url)
|
10
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
11
|
+
http.use_ssl = true
|
12
|
+
uri.query = URI.encode_www_form(params)
|
13
|
+
Net::HTTP.get_response(uri)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class VWO
|
6
|
+
module Common
|
7
|
+
# Schema for verifying the settings_file provided by the customer
|
8
|
+
module Schema
|
9
|
+
SETTINGS_FILE_SCHEMA = {
|
10
|
+
type: 'object',
|
11
|
+
properties: {
|
12
|
+
version: {
|
13
|
+
type: %w[number string]
|
14
|
+
},
|
15
|
+
accountId: {
|
16
|
+
type: %w[number string]
|
17
|
+
},
|
18
|
+
campaigns: {
|
19
|
+
if: {
|
20
|
+
type: 'array'
|
21
|
+
},
|
22
|
+
then: {
|
23
|
+
minItems: 1,
|
24
|
+
items: {
|
25
|
+
'$ref' => '#/definitions/campaign_object_schema'
|
26
|
+
}
|
27
|
+
},
|
28
|
+
else: {
|
29
|
+
type: 'object',
|
30
|
+
maxProperties: 0
|
31
|
+
}
|
32
|
+
}
|
33
|
+
},
|
34
|
+
definitions: {
|
35
|
+
campaign_variation_schema: {
|
36
|
+
type: 'object',
|
37
|
+
properties: {
|
38
|
+
id: {
|
39
|
+
type: %w[number string]
|
40
|
+
},
|
41
|
+
name: {
|
42
|
+
type: ['string']
|
43
|
+
},
|
44
|
+
weight: {
|
45
|
+
type: %w[number string]
|
46
|
+
}
|
47
|
+
},
|
48
|
+
required: %w[id name weight]
|
49
|
+
},
|
50
|
+
campaign_object_schema: {
|
51
|
+
type: 'object',
|
52
|
+
properties: {
|
53
|
+
id: {
|
54
|
+
type: %w[number string]
|
55
|
+
},
|
56
|
+
key: {
|
57
|
+
type: ['string']
|
58
|
+
},
|
59
|
+
status: {
|
60
|
+
type: ['string']
|
61
|
+
},
|
62
|
+
percentTraffic: {
|
63
|
+
type: ['number']
|
64
|
+
},
|
65
|
+
variations: {
|
66
|
+
type: 'array',
|
67
|
+
items: {
|
68
|
+
'$ref' => '#/definitions/campaign_variation_schema'
|
69
|
+
}
|
70
|
+
},
|
71
|
+
minItems: 2
|
72
|
+
}
|
73
|
+
},
|
74
|
+
required: %w[
|
75
|
+
id
|
76
|
+
key
|
77
|
+
status
|
78
|
+
percentTraffic
|
79
|
+
variations
|
80
|
+
]
|
81
|
+
},
|
82
|
+
required: %w[
|
83
|
+
version
|
84
|
+
accountId
|
85
|
+
campaigns
|
86
|
+
]
|
87
|
+
}.freeze
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|