vwo-sdk 1.3.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.rb +348 -0
- data/lib/vwo/constants.rb +63 -0
- data/lib/vwo/core/bucketer.rb +177 -0
- data/lib/vwo/core/variation_decider.rb +308 -0
- data/lib/vwo/enums.rb +115 -0
- data/lib/vwo/logger.rb +37 -0
- data/lib/vwo/schemas/settings_file.rb +102 -0
- data/lib/vwo/services/event_dispatcher.rb +82 -0
- data/lib/vwo/services/settings_file_manager.rb +84 -0
- data/lib/vwo/services/settings_file_processor.rb +54 -0
- data/lib/vwo/user_storage.rb +36 -0
- data/lib/vwo/utils/campaign.rb +125 -0
- data/lib/vwo/utils/function.rb +39 -0
- data/lib/vwo/utils/impression.rb +98 -0
- data/lib/vwo/utils/request.rb +31 -0
- data/lib/vwo/utils/uuid.rb +93 -0
- data/lib/vwo/utils/validations.rb +56 -0
- metadata +117 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
# Copyright 2019 Wingify Software Pvt. Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# frozen_string_literal: true
|
16
|
+
|
17
|
+
require 'json'
|
18
|
+
|
19
|
+
class VWO
|
20
|
+
# Schema for verifying the settings_file provided by the customer
|
21
|
+
module Schema
|
22
|
+
SETTINGS_FILE_SCHEMA = {
|
23
|
+
type: 'object',
|
24
|
+
properties: {
|
25
|
+
version: {
|
26
|
+
type: %w[number string]
|
27
|
+
},
|
28
|
+
accountId: {
|
29
|
+
type: %w[number string]
|
30
|
+
},
|
31
|
+
campaigns: {
|
32
|
+
if: {
|
33
|
+
type: 'array'
|
34
|
+
},
|
35
|
+
then: {
|
36
|
+
minItems: 1,
|
37
|
+
items: {
|
38
|
+
'$ref' => '#/definitions/campaign_object_schema'
|
39
|
+
}
|
40
|
+
},
|
41
|
+
else: {
|
42
|
+
type: 'object',
|
43
|
+
maxProperties: 0
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
definitions: {
|
48
|
+
campaign_variation_schema: {
|
49
|
+
type: 'object',
|
50
|
+
properties: {
|
51
|
+
id: {
|
52
|
+
type: %w[number string]
|
53
|
+
},
|
54
|
+
name: {
|
55
|
+
type: ['string']
|
56
|
+
},
|
57
|
+
weight: {
|
58
|
+
type: %w[number string]
|
59
|
+
}
|
60
|
+
},
|
61
|
+
required: %w[id name weight]
|
62
|
+
},
|
63
|
+
campaign_object_schema: {
|
64
|
+
type: 'object',
|
65
|
+
properties: {
|
66
|
+
id: {
|
67
|
+
type: %w[number string]
|
68
|
+
},
|
69
|
+
key: {
|
70
|
+
type: ['string']
|
71
|
+
},
|
72
|
+
status: {
|
73
|
+
type: ['string']
|
74
|
+
},
|
75
|
+
percentTraffic: {
|
76
|
+
type: ['number']
|
77
|
+
},
|
78
|
+
variations: {
|
79
|
+
type: 'array',
|
80
|
+
items: {
|
81
|
+
'$ref' => '#/definitions/campaign_variation_schema'
|
82
|
+
}
|
83
|
+
},
|
84
|
+
minItems: 2
|
85
|
+
}
|
86
|
+
},
|
87
|
+
required: %w[
|
88
|
+
id
|
89
|
+
key
|
90
|
+
status
|
91
|
+
percentTraffic
|
92
|
+
variations
|
93
|
+
]
|
94
|
+
},
|
95
|
+
required: %w[
|
96
|
+
version
|
97
|
+
accountId
|
98
|
+
campaigns
|
99
|
+
]
|
100
|
+
}.freeze
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Copyright 2019 Wingify Software Pvt. Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# frozen_string_literal: true
|
16
|
+
|
17
|
+
require_relative '../logger'
|
18
|
+
require_relative '../enums'
|
19
|
+
require_relative '../utils/request'
|
20
|
+
|
21
|
+
class VWO
|
22
|
+
module Services
|
23
|
+
class EventDispatcher
|
24
|
+
include VWO::Enums
|
25
|
+
|
26
|
+
EXCLUDE_KEYS = ['url'].freeze
|
27
|
+
|
28
|
+
# Initialize the dispatcher with logger and development mode
|
29
|
+
#
|
30
|
+
# @param [Boolean] : To specify whether the request
|
31
|
+
# to our server should be made or not.
|
32
|
+
#
|
33
|
+
def initialize(is_development_mode = false)
|
34
|
+
@logger = VWO::Logger.get_instance
|
35
|
+
@is_development_mode = is_development_mode
|
36
|
+
end
|
37
|
+
|
38
|
+
# Dispatch the impression event having properties object only if dev-mode is OFF
|
39
|
+
#
|
40
|
+
# @param[Hash] :properties hash having impression properties
|
41
|
+
# the request to be dispatched to the VWO server
|
42
|
+
# @return[Boolean]
|
43
|
+
#
|
44
|
+
def dispatch(impression)
|
45
|
+
return true if @is_development_mode
|
46
|
+
|
47
|
+
modified_event = impression.reject do |key, _value|
|
48
|
+
EXCLUDE_KEYS.include?(key)
|
49
|
+
end
|
50
|
+
|
51
|
+
resp = VWO::Utils::Request.get(impression['url'], modified_event)
|
52
|
+
if resp.code == '200'
|
53
|
+
@logger.log(
|
54
|
+
LogLevelEnum::INFO,
|
55
|
+
format(
|
56
|
+
LogMessageEnum::InfoMessages::IMPRESSION_SUCCESS,
|
57
|
+
file: FileNameEnum::EventDispatcher,
|
58
|
+
end_point: impression[:url],
|
59
|
+
campaign_id: impression[:experiment_id],
|
60
|
+
user_id: impression[:uId],
|
61
|
+
account_id: impression[:account_id],
|
62
|
+
variation_id: impression[:combination]
|
63
|
+
)
|
64
|
+
)
|
65
|
+
return true
|
66
|
+
else
|
67
|
+
@logger.log(
|
68
|
+
LogLevelEnum::ERROR,
|
69
|
+
format(LogMessageEnum::ErrorMessages::IMPRESSION_FAILED, file: FileNameEnum::EventDispatcher, end_point: impression['url'])
|
70
|
+
)
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
rescue StandardError
|
74
|
+
@logger.log(
|
75
|
+
LogLevelEnum::ERROR,
|
76
|
+
format(LogMessageEnum::ErrorMessages::IMPRESSION_FAILED, file: FileNameEnum::EventDispatcher, end_point: impression['url'])
|
77
|
+
)
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright 2019 Wingify Software Pvt. Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# frozen_string_literal: true
|
16
|
+
|
17
|
+
require_relative '../utils/function'
|
18
|
+
require_relative '../utils/request'
|
19
|
+
require_relative '../utils/validations'
|
20
|
+
require_relative '../constants'
|
21
|
+
|
22
|
+
class VWO
|
23
|
+
module Services
|
24
|
+
class SettingsFileManager
|
25
|
+
include ::VWO::Utils::Validations
|
26
|
+
include ::VWO::Utils::Function
|
27
|
+
|
28
|
+
PROTOCOL = 'https'
|
29
|
+
HOSTNAME = ::VWO::CONSTANTS::ENDPOINTS::BASE_URL
|
30
|
+
PATH = ::VWO::CONSTANTS::ENDPOINTS::ACCOUNT_SETTINGS
|
31
|
+
|
32
|
+
def initialize(account_id, sdk_key)
|
33
|
+
@account_id = account_id
|
34
|
+
@sdk_key = sdk_key
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get Settings file method to retrieve settings_file for customer from VWO server
|
38
|
+
# @param [string]: Account ID of user
|
39
|
+
# @param [string]: Unique sdk key for user,
|
40
|
+
# can be retrieved from VWO app
|
41
|
+
# @return[string]: JSON - settings_file,
|
42
|
+
# as received from the server,
|
43
|
+
# nil if no settings_file is found or sdk_key is incorrect
|
44
|
+
|
45
|
+
def get_settings_file
|
46
|
+
is_valid_key = valid_number?(@account_id) || valid_string?(@account_id)
|
47
|
+
|
48
|
+
unless is_valid_key && valid_string?(@sdk_key)
|
49
|
+
STDERR.puts 'account_id and sdk_key are required for fetching account settings. Aborting!'
|
50
|
+
return '{}'
|
51
|
+
end
|
52
|
+
|
53
|
+
vwo_server_url = "#{PROTOCOL}://#{HOSTNAME}#{PATH}"
|
54
|
+
|
55
|
+
settings_file_response = ::VWO::Utils::Request.get(vwo_server_url, params)
|
56
|
+
|
57
|
+
if settings_file_response.code != '200'
|
58
|
+
message = <<-DOC
|
59
|
+
Request failed for fetching account settings.
|
60
|
+
Got Status Code: #{settings_file_response.code}
|
61
|
+
and message: #{settings_file_response.body}.
|
62
|
+
DOC
|
63
|
+
STDERR.puts message
|
64
|
+
return
|
65
|
+
end
|
66
|
+
settings_file_response.body
|
67
|
+
rescue StandardError => e
|
68
|
+
STDERR.puts "Error fetching Settings File #{e}"
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def params
|
74
|
+
{
|
75
|
+
a: @account_id,
|
76
|
+
i: @sdk_key,
|
77
|
+
r: get_random_number,
|
78
|
+
platform: 'server',
|
79
|
+
'api-version' => 1
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copyright 2019 Wingify Software Pvt. Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# frozen_string_literal: true
|
16
|
+
|
17
|
+
require_relative '../logger'
|
18
|
+
require_relative '../enums'
|
19
|
+
require_relative '../utils/campaign'
|
20
|
+
|
21
|
+
class VWO
|
22
|
+
module Services
|
23
|
+
class SettingsFileProcessor
|
24
|
+
include VWO::Enums
|
25
|
+
include VWO::Utils::Campaign
|
26
|
+
|
27
|
+
# Method to initialize settings_file and logger
|
28
|
+
#
|
29
|
+
# @params
|
30
|
+
# settings_file (Hash): Hash object of setting
|
31
|
+
# representing the settings_file.
|
32
|
+
|
33
|
+
def initialize(settings_file)
|
34
|
+
@settings_file = JSON.parse(settings_file)
|
35
|
+
@logger = VWO::Logger.get_instance
|
36
|
+
end
|
37
|
+
|
38
|
+
# Processes the settings_file, assigns variation allocation range
|
39
|
+
def process_settings_file
|
40
|
+
(@settings_file['campaigns'] || []).each do |campaign|
|
41
|
+
set_variation_allocation(campaign)
|
42
|
+
end
|
43
|
+
@logger.log(
|
44
|
+
LogLevelEnum::DEBUG,
|
45
|
+
format(LogMessageEnum::DebugMessages::SETTINGS_FILE_PROCESSED, file: FileNameEnum::SettingsFileProcessor)
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_settings_file
|
50
|
+
@settings_file
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright 2019 Wingify Software Pvt. Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# frozen_string_literal: true
|
16
|
+
|
17
|
+
class VWO
|
18
|
+
# UserStorage Class is used to store user-variation mapping.
|
19
|
+
# Override this class to implement your own functionality.
|
20
|
+
# SDK will ensure to use this while bucketing a user into a variation.
|
21
|
+
|
22
|
+
class UserStorage
|
23
|
+
# To retrieve the stored variation for the user_id.
|
24
|
+
#
|
25
|
+
# @param[String] :user_id ID for user that needs to be retrieved.
|
26
|
+
# @param[String] :_campaign_key Unique campaign key
|
27
|
+
# @return[Hash] :user_data User's data.
|
28
|
+
#
|
29
|
+
def get(_user_id, _campaign_key); end
|
30
|
+
|
31
|
+
# To store the the user variation-mapping
|
32
|
+
# @param[Hash] :user_data
|
33
|
+
#
|
34
|
+
def set(_user_data); end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# Copyright 2019 Wingify Software Pvt. Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# frozen_string_literal: true
|
16
|
+
|
17
|
+
require_relative '../logger'
|
18
|
+
require_relative '../enums'
|
19
|
+
require_relative '../constants'
|
20
|
+
|
21
|
+
# Utility module for processing VWO campaigns
|
22
|
+
class VWO
|
23
|
+
module Utils
|
24
|
+
module Campaign
|
25
|
+
include VWO::Enums
|
26
|
+
include VWO::CONSTANTS
|
27
|
+
|
28
|
+
# Finds and Returns campaign from given campaign_key.
|
29
|
+
#
|
30
|
+
# @param[Hash] :settings_file Settings file
|
31
|
+
# @param[String] :campaign_key Campaign identifier key
|
32
|
+
# @return[Hash] :campaign object
|
33
|
+
|
34
|
+
def get_campaign(settings_file, campaign_key)
|
35
|
+
(settings_file['campaigns'] || []).find do |campaign|
|
36
|
+
campaign['key'] == campaign_key
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sets variation allocation range in the provided campaign
|
41
|
+
#
|
42
|
+
# @param [Hash]: Campaign object
|
43
|
+
|
44
|
+
def set_variation_allocation(campaign)
|
45
|
+
current_allocation = 0
|
46
|
+
campaign['variations'].each do |variation|
|
47
|
+
step_factor = get_variation_bucketing_range(variation['weight'])
|
48
|
+
if step_factor > 0
|
49
|
+
start_range = current_allocation + 1
|
50
|
+
end_range = current_allocation + step_factor
|
51
|
+
variation['start_variation_allocation'] = start_range
|
52
|
+
variation['end_variation_allocation'] = end_range
|
53
|
+
current_allocation += step_factor
|
54
|
+
else
|
55
|
+
variation['start_variation_allocation'] = -1
|
56
|
+
variation['end_variation_allocation'] = -1
|
57
|
+
end
|
58
|
+
|
59
|
+
VWO::Logger.get_instance.log(
|
60
|
+
LogLevelEnum::INFO,
|
61
|
+
format(
|
62
|
+
LogMessageEnum::InfoMessages::VARIATION_RANGE_ALLOCATION,
|
63
|
+
file: FileNameEnum::CampaignUtil,
|
64
|
+
campaign_key: campaign['key'],
|
65
|
+
variation_name: variation['name'],
|
66
|
+
variation_weight: variation['weight'],
|
67
|
+
start: variation['start_variation_allocation'],
|
68
|
+
end: variation['end_variation_allocation']
|
69
|
+
)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns goal from given campaign_key and gaol_identifier.
|
75
|
+
# @param[Hash] :settings_file Settings file
|
76
|
+
# @param[String] :campaign_key Campaign identifier key
|
77
|
+
# @param[String] :goal_identifier Goal identifier
|
78
|
+
#
|
79
|
+
# @return[Hash] Goal corresponding to Goal_identifier in respective campaign
|
80
|
+
|
81
|
+
def get_campaign_goal(settings_file, campaign_key, goal_identifier)
|
82
|
+
return unless settings_file && campaign_key && goal_identifier
|
83
|
+
|
84
|
+
campaign = get_campaign(settings_file, campaign_key)
|
85
|
+
return unless campaign
|
86
|
+
|
87
|
+
campaign['goals'].find do |goal|
|
88
|
+
goal['identifier'] == goal_identifier
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# Returns the bucket size of variation.
|
95
|
+
# @param (Number): weight of variation
|
96
|
+
# @return (Integer): Bucket start range of Variation
|
97
|
+
|
98
|
+
def get_variation_bucketing_range(weight)
|
99
|
+
return 0 if weight.nil? || weight == 0
|
100
|
+
|
101
|
+
start_range = (weight * 100).ceil.to_i
|
102
|
+
[start_range, MAX_TRAFFIC_VALUE].min
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns variation from given campaign_key and variation_name.
|
106
|
+
#
|
107
|
+
# @param[Hash] :settings_file Settings file
|
108
|
+
# @param[Hash] :campaign_key Campaign identifier key
|
109
|
+
# @param[String] :variation_name Variation identifier
|
110
|
+
#
|
111
|
+
# @return[Hash] Variation corresponding to variation_name in respective campaign
|
112
|
+
|
113
|
+
def get_campaign_variation(settings_file, campaign_key, variation_name)
|
114
|
+
return unless settings_file && campaign_key && variation_name
|
115
|
+
|
116
|
+
campaign = get_campaign(settings_file, campaign_key)
|
117
|
+
return unless campaign
|
118
|
+
|
119
|
+
campaign['variations'].find do |variation|
|
120
|
+
variation['name'] == variation_name
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|