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 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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class VWO
4
+ module Common
5
+ class Utils
6
+ def self.get_random_number
7
+ rand
8
+ end
9
+ end
10
+ end
11
+ end