vwo-fme-ruby-sdk 1.0.0 → 1.1.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 +4 -4
- data/lib/vwo/api/get_flag.rb +236 -0
- data/lib/vwo/api/set_attribute.rb +57 -0
- data/lib/vwo/api/track_event.rb +77 -0
- data/lib/vwo/constants/constants.rb +54 -0
- data/lib/vwo/decorators/storage_decorator.rb +86 -0
- data/lib/vwo/{utils/logger_helper.rb → enums/api_enum.rb} +5 -10
- data/lib/vwo/enums/campaign_type_enum.rb +19 -0
- data/lib/vwo/enums/decision_types_enum.rb +18 -0
- data/lib/vwo/enums/event_enum.rb +19 -0
- data/lib/vwo/enums/headers_enum.rb +20 -0
- data/lib/vwo/enums/hooks_enum.rb +17 -0
- data/lib/vwo/enums/http_method_enum.rb +18 -0
- data/lib/vwo/enums/log_level_enum.rb +21 -0
- data/lib/vwo/enums/status_enum.rb +19 -0
- data/lib/vwo/enums/storage_enum.rb +22 -0
- data/lib/vwo/enums/url_enum.rb +21 -0
- data/lib/vwo/models/campaign/campaign_model.rb +192 -0
- data/lib/vwo/models/campaign/feature_model.rb +111 -0
- data/lib/vwo/models/campaign/impact_campaign_model.rb +38 -0
- data/lib/vwo/models/campaign/metric_model.rb +44 -0
- data/lib/vwo/models/campaign/rule_model.rb +56 -0
- data/lib/vwo/models/campaign/variable_model.rb +51 -0
- data/lib/vwo/models/campaign/variation_model.rb +137 -0
- data/lib/vwo/models/gateway_service_model.rb +39 -0
- data/lib/vwo/models/schemas/settings_schema_validation.rb +102 -0
- data/lib/vwo/models/settings/settings_model.rb +85 -0
- data/lib/vwo/models/storage/storage_data_model.rb +44 -0
- data/lib/vwo/models/user/context_model.rb +100 -0
- data/lib/vwo/models/user/context_vwo_model.rb +38 -0
- data/lib/vwo/{utils/feature_flag_response.rb → models/user/get_flag_response.rb} +14 -14
- data/lib/vwo/models/vwo_options_model.rb +107 -0
- data/lib/vwo/packages/decision_maker/decision_maker.rb +60 -0
- data/lib/vwo/packages/logger/core/log_manager.rb +90 -0
- data/lib/vwo/packages/logger/core/transport_manager.rb +87 -0
- data/lib/vwo/packages/logger/log_message_builder.rb +70 -0
- data/lib/vwo/packages/logger/logger.rb +38 -0
- data/lib/vwo/packages/logger/transports/console_transport.rb +49 -0
- data/lib/vwo/packages/network_layer/client/network_client.rb +107 -0
- data/lib/vwo/packages/network_layer/handlers/request_handler.rb +37 -0
- data/lib/vwo/packages/network_layer/manager/network_manager.rb +78 -0
- data/lib/vwo/packages/network_layer/models/global_request_model.rb +105 -0
- data/lib/vwo/packages/network_layer/models/request_model.rb +145 -0
- data/lib/vwo/packages/network_layer/models/response_model.rb +45 -0
- data/lib/vwo/packages/segmentation_evaluator/core/segmentation_manager.rb +76 -0
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +29 -0
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +26 -0
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +30 -0
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +210 -0
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +198 -0
- data/lib/vwo/packages/segmentation_evaluator/utils/segment_util.rb +44 -0
- data/lib/vwo/{constants.rb → packages/storage/connector.rb} +12 -10
- data/lib/vwo/packages/storage/storage.rb +45 -0
- data/lib/vwo/services/campaign_decision_service.rb +153 -0
- data/lib/vwo/services/hooks_service.rb +51 -0
- data/lib/vwo/services/logger_service.rb +83 -0
- data/lib/vwo/services/settings_service.rb +120 -0
- data/lib/vwo/services/storage_service.rb +65 -0
- data/lib/vwo/utils/campaign_util.rb +249 -0
- data/lib/vwo/utils/data_type_util.rb +105 -0
- data/lib/vwo/utils/decision_util.rb +253 -0
- data/lib/vwo/utils/function_util.rb +123 -0
- data/lib/vwo/utils/gateway_service_util.rb +101 -0
- data/lib/vwo/utils/impression_util.rb +49 -0
- data/lib/vwo/utils/log_message_util.rb +42 -0
- data/lib/vwo/utils/meg_util.rb +350 -0
- data/lib/vwo/utils/network_util.rb +235 -0
- data/lib/vwo/utils/rule_evaluation_util.rb +57 -0
- data/lib/vwo/utils/settings_util.rb +38 -0
- data/lib/vwo/utils/url_util.rb +46 -0
- data/lib/vwo/utils/uuid_util.rb +55 -0
- data/lib/vwo/vwo_builder.rb +156 -11
- data/lib/vwo/vwo_client.rb +163 -113
- data/lib/vwo.rb +49 -31
- metadata +187 -9
- data/lib/vwo/utils/request.rb +0 -89
@@ -0,0 +1,120 @@
|
|
1
|
+
# Copyright 2025 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
|
+
require 'json'
|
16
|
+
require_relative '../packages/network_layer/manager/network_manager'
|
17
|
+
require_relative '../packages/network_layer/models/request_model'
|
18
|
+
require_relative '../packages/network_layer/models/response_model'
|
19
|
+
require_relative '../constants/constants'
|
20
|
+
require_relative '../utils/network_util'
|
21
|
+
require_relative '../services/logger_service'
|
22
|
+
require_relative '../enums/log_level_enum'
|
23
|
+
require_relative '../models/schemas/settings_schema_validation'
|
24
|
+
|
25
|
+
class SettingsService
|
26
|
+
attr_accessor :sdk_key, :account_id, :expiry, :network_timeout, :hostname, :port, :protocol, :is_gateway_service_provided
|
27
|
+
|
28
|
+
class << self
|
29
|
+
attr_accessor :instance
|
30
|
+
|
31
|
+
def get_instance
|
32
|
+
@instance ||= SettingsService.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(options)
|
37
|
+
@sdk_key = options[:sdk_key]
|
38
|
+
@account_id = options[:account_id]
|
39
|
+
@expiry = options.dig(:settings, :expiry) || Constants::SETTINGS_EXPIRY
|
40
|
+
@network_timeout = options.dig(:settings, :timeout) || Constants::SETTINGS_TIMEOUT
|
41
|
+
|
42
|
+
if options[:gateway_service] && options[:gateway_service][:url]
|
43
|
+
parsed_url = URI.parse(options[:gateway_service][:url].start_with?(Constants::HTTP_PROTOCOL) || options[:gateway_service][:url].start_with?(Constants::HTTPS_PROTOCOL) ? options[:gateway_service][:url] : "#{Constants::HTTPS_PROTOCOL}#{options[:gateway_service][:url]}")
|
44
|
+
@hostname = parsed_url.hostname
|
45
|
+
@protocol = parsed_url.scheme
|
46
|
+
@port = parsed_url.port || options.dig(:gateway_service, :port)
|
47
|
+
@is_gateway_service_provided = true
|
48
|
+
else
|
49
|
+
@hostname = Constants::HOST_NAME
|
50
|
+
@protocol = Constants::HTTPS_PROTOCOL
|
51
|
+
end
|
52
|
+
|
53
|
+
LoggerService.log(LogLevelEnum::DEBUG, "SERVICE_INITIALIZED", { service: 'Settings Manager' })
|
54
|
+
SettingsService.instance = self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Fetch settings and cache them in storage.
|
58
|
+
# @return [SettingsModel] The fetched settings
|
59
|
+
def fetch_settings_and_cache_in_storage
|
60
|
+
begin
|
61
|
+
response = fetch_settings
|
62
|
+
response
|
63
|
+
rescue => e
|
64
|
+
LoggerService.log(LogLevelEnum::ERROR, "SETTINGS_FETCH_ERROR", { err: e.message })
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Fetch settings from the server.
|
70
|
+
# @param is_via_webhook [Boolean] Whether to fetch settings via webhook
|
71
|
+
# @return [SettingsModel] The fetched settings
|
72
|
+
def fetch_settings(is_via_webhook = false)
|
73
|
+
if @sdk_key.nil? || @account_id.nil?
|
74
|
+
LoggerService.log(LogLevelEnum::ERROR, "sdkKey is required for fetching account settings. Aborting!", nil)
|
75
|
+
end
|
76
|
+
|
77
|
+
network_instance = NetworkManager.instance
|
78
|
+
options = NetworkUtil.get_settings_path(@sdk_key, @account_id)
|
79
|
+
|
80
|
+
options['api-version'] = Constants::API_VERSION
|
81
|
+
options[:source] = 'prod'
|
82
|
+
|
83
|
+
# When using gateway service, always fetch from SETTINGS_ENDPOINT since the gateway maintains the latest settings
|
84
|
+
if @is_gateway_service_provided
|
85
|
+
path = Constants::SETTINGS_ENDPOINT
|
86
|
+
else
|
87
|
+
path = is_via_webhook ? Constants::WEBHOOK_SETTINGS_ENDPOINT : Constants::SETTINGS_ENDPOINT
|
88
|
+
end
|
89
|
+
|
90
|
+
request = RequestModel.new(@hostname, "GET", path, options, nil, nil, @protocol, @port)
|
91
|
+
request.set_timeout(@network_timeout)
|
92
|
+
|
93
|
+
begin
|
94
|
+
response = network_instance.get(request)
|
95
|
+
response.get_data
|
96
|
+
rescue => e
|
97
|
+
LoggerService.log(LogLevelEnum::ERROR, "Error fetching settings: #{e.message}", nil)
|
98
|
+
raise e
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get settings (either from storage or by forcing fetch).
|
103
|
+
# @param force_fetch [Boolean] Whether to force fetch the settings
|
104
|
+
# @return [SettingsModel] The fetched settings
|
105
|
+
def get_settings(force_fetch = false)
|
106
|
+
if force_fetch
|
107
|
+
fetch_settings_and_cache_in_storage
|
108
|
+
else
|
109
|
+
settings = fetch_settings_and_cache_in_storage
|
110
|
+
is_valid = SettingsSchema.new.is_settings_valid(settings)
|
111
|
+
if is_valid
|
112
|
+
LoggerService.log(LogLevelEnum::INFO, "SETTINGS_FETCH_SUCCESS")
|
113
|
+
settings
|
114
|
+
else
|
115
|
+
LoggerService.log(LogLevelEnum::ERROR, "SETTINGS_SCHEMA_INVALID")
|
116
|
+
{}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Copyright 2025 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
|
+
require_relative '../enums/storage_enum'
|
16
|
+
require_relative '../models/user/context_model'
|
17
|
+
require_relative '../packages/storage/storage'
|
18
|
+
require_relative '../services/logger_service'
|
19
|
+
require_relative '../enums/log_level_enum'
|
20
|
+
require_relative '../utils/data_type_util'
|
21
|
+
|
22
|
+
class StorageService
|
23
|
+
attr_accessor :storage_data
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@storage_data = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Retrieves data from storage based on the feature key and user ID.
|
30
|
+
# @param feature_key [String] The key to identify the feature data.
|
31
|
+
# @param context [ContextModel] The user context containing an ID.
|
32
|
+
# @return [Hash] The data retrieved or a storage status enum.
|
33
|
+
def get_data_from_storage(feature_key, context)
|
34
|
+
storage_instance = Storage.instance.get_connector
|
35
|
+
|
36
|
+
# Check if the storage instance is available
|
37
|
+
return StorageEnum::STORAGE_UNDEFINED if DataTypeUtil.is_null(storage_instance) || DataTypeUtil.is_undefined(storage_instance)
|
38
|
+
|
39
|
+
begin
|
40
|
+
data = storage_instance.get(feature_key, context.get_id)
|
41
|
+
return data.nil? ? StorageEnum::NO_DATA_FOUND : data
|
42
|
+
rescue StandardError => e
|
43
|
+
LoggerService.log(LogLevelEnum::ERROR, "STORED_DATA_ERROR", { err: e.message })
|
44
|
+
return StorageEnum::NO_DATA_FOUND
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Stores data in the storage.
|
49
|
+
# @param data [Hash] The data to be stored.
|
50
|
+
# @return [Boolean] True if data is successfully stored, otherwise false.
|
51
|
+
def set_data_in_storage(data)
|
52
|
+
storage_instance = Storage.instance.get_connector
|
53
|
+
|
54
|
+
# Check if the storage instance is available
|
55
|
+
return false if storage_instance.nil?
|
56
|
+
|
57
|
+
begin
|
58
|
+
storage_instance.set(data)
|
59
|
+
return true
|
60
|
+
rescue StandardError
|
61
|
+
LoggerService.log(LogLevelEnum::ERROR, "STORED_DATA_ERROR", { err: e.message })
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# Copyright 2025 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
|
+
require_relative '../constants/constants'
|
16
|
+
require_relative '../enums/campaign_type_enum'
|
17
|
+
require_relative '../models/campaign/campaign_model'
|
18
|
+
require_relative '../models/campaign/feature_model'
|
19
|
+
require_relative '../models/campaign/variation_model'
|
20
|
+
require_relative '../models/settings/settings_model'
|
21
|
+
require_relative '../services/logger_service'
|
22
|
+
require_relative '../enums/log_level_enum'
|
23
|
+
require_relative '../models/campaign/rule_model'
|
24
|
+
|
25
|
+
module CampaignUtil
|
26
|
+
# Sets the variation allocation for a campaign
|
27
|
+
# @param campaign [CampaignModel] The campaign to set the variation allocation for
|
28
|
+
def self.set_variation_allocation(campaign)
|
29
|
+
if [CampaignTypeEnum::ROLLOUT, CampaignTypeEnum::PERSONALIZE].include?(campaign.get_type)
|
30
|
+
handle_rollout_campaign(campaign)
|
31
|
+
else
|
32
|
+
current_allocation = 0
|
33
|
+
campaign.get_variations.each do |variation|
|
34
|
+
step_factor = assign_range_values(variation, current_allocation)
|
35
|
+
current_allocation += step_factor
|
36
|
+
|
37
|
+
LoggerService.log(LogLevelEnum::INFO, "VARIATION_RANGE_ALLOCATION", {
|
38
|
+
variationKey: variation.get_key,
|
39
|
+
campaignKey: campaign.get_key,
|
40
|
+
variationWeight: variation.get_weight,
|
41
|
+
startRange: variation.get_start_range_variation,
|
42
|
+
endRange: variation.get_end_range_variation
|
43
|
+
})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Assigns start and end range values to a variation
|
49
|
+
# @param variation [VariationModel] The variation to assign the start and end range values to
|
50
|
+
# @param current_allocation [Integer] The current allocation
|
51
|
+
# @return [Integer] The step factor
|
52
|
+
def self.assign_range_values(variation, current_allocation)
|
53
|
+
step_factor = get_variation_bucket_range(variation.get_weight)
|
54
|
+
|
55
|
+
if step_factor > 0
|
56
|
+
variation.set_start_range(current_allocation + 1)
|
57
|
+
variation.set_end_range(current_allocation + step_factor)
|
58
|
+
else
|
59
|
+
variation.set_start_range(-1)
|
60
|
+
variation.set_end_range(-1)
|
61
|
+
end
|
62
|
+
|
63
|
+
step_factor
|
64
|
+
end
|
65
|
+
|
66
|
+
# Scales variation weights to sum up to 100%
|
67
|
+
# @param variations [Array<VariationModel>] The variations to scale the weights of
|
68
|
+
# @return [Array<VariationModel>] The scaled variations
|
69
|
+
def self.scale_variation_weights(variations)
|
70
|
+
total_weight = variations.sum(&:weight)
|
71
|
+
|
72
|
+
if total_weight.zero?
|
73
|
+
equal_weight = 100.0 / variations.length
|
74
|
+
variations.each { |variation| variation.weight = equal_weight }
|
75
|
+
else
|
76
|
+
variations.each { |variation| variation.weight = (variation.weight / total_weight) * 100 }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Generates a bucketing seed based on user ID and campaign
|
81
|
+
# @param user_id [String] The ID of the user
|
82
|
+
# @param campaign [CampaignModel] The campaign to generate the bucketing seed for
|
83
|
+
# @param group_id [String] The ID of the group
|
84
|
+
# @return [String] The bucketing seed
|
85
|
+
def self.get_bucketing_seed(user_id, campaign, group_id = nil)
|
86
|
+
return "#{group_id}_#{user_id}" if group_id
|
87
|
+
|
88
|
+
is_rollout_or_personalize = [CampaignTypeEnum::ROLLOUT, CampaignTypeEnum::PERSONALIZE].include?(campaign.get_type)
|
89
|
+
salt = is_rollout_or_personalize ? campaign.get_variations.first.get_salt : campaign.get_salt
|
90
|
+
|
91
|
+
salt ? "#{salt}_#{user_id}" : "#{campaign.get_id}_#{user_id}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Retrieves variation from campaign key
|
95
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
96
|
+
# @param campaign_key [String] The key of the campaign
|
97
|
+
# @param variation_id [Integer] The ID of the variation
|
98
|
+
# @return [VariationModel] The variation
|
99
|
+
def self.get_variation_from_campaign_key(settings, campaign_key, variation_id)
|
100
|
+
campaign = settings.get_campaigns.find { |c| c.get_key == campaign_key }
|
101
|
+
return nil unless campaign
|
102
|
+
|
103
|
+
variation = campaign.get_variations.find { |v| v.get_id == variation_id }
|
104
|
+
variation ? VariationModel.new.model_from_dictionary(variation) : nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# Sets campaign allocation ranges
|
108
|
+
# @param campaigns [Array<CampaignModel>] The campaigns to set the allocation ranges for
|
109
|
+
def self.set_campaign_allocation(campaigns)
|
110
|
+
current_allocation = 0
|
111
|
+
|
112
|
+
campaigns.each do |campaign|
|
113
|
+
step_factor = assign_range_values_meg(campaign, current_allocation)
|
114
|
+
current_allocation += step_factor
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Retrieves campaign group details if part of a group
|
119
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
120
|
+
# @param campaign_id [Integer] The ID of the campaign
|
121
|
+
# @param variation_id [Integer] The ID of the variation
|
122
|
+
# @return [Hash] The group details
|
123
|
+
def self.get_group_details_if_campaign_part_of_it(settings, campaign_id, variation_id = nil)
|
124
|
+
campaign_to_check = variation_id ? "#{campaign_id}_#{variation_id}" : campaign_id.to_s
|
125
|
+
|
126
|
+
if settings.get_campaign_groups.key?(campaign_to_check)
|
127
|
+
group_id = settings.get_campaign_groups[campaign_to_check]
|
128
|
+
{ group_id: group_id, group_name: settings.get_groups[group_id.to_s][:name.to_s]}
|
129
|
+
else
|
130
|
+
{}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Finds groups associated with a feature
|
135
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
136
|
+
# @param feature_key [String] The key of the feature
|
137
|
+
# @return [Array] The groups associated with the feature
|
138
|
+
def self.find_groups_feature_part_of(settings, feature_key)
|
139
|
+
rule_array = []
|
140
|
+
settings.get_features.each do |feature|
|
141
|
+
if feature.get_key == feature_key
|
142
|
+
rule_array.concat(feature.get_rules)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
groups = []
|
147
|
+
rule_array.each do |rule|
|
148
|
+
group = get_group_details_if_campaign_part_of_it(settings, rule.get_campaign_id, rule.get_type == CampaignTypeEnum::PERSONALIZE ? rule.get_variation_id : nil)
|
149
|
+
groups << group unless group.empty? || groups.any? { |g| g[:group_id] == group[:group_id] }
|
150
|
+
end
|
151
|
+
|
152
|
+
groups
|
153
|
+
end
|
154
|
+
|
155
|
+
# Retrieves campaigns by group ID
|
156
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
157
|
+
# @param group_id [String] The ID of the group
|
158
|
+
# @return [Array] The campaigns associated with the group
|
159
|
+
def self.get_campaigns_by_group_id(settings, group_id)
|
160
|
+
settings.get_groups[group_id.to_s]&.fetch(:campaigns.to_s, []) || []
|
161
|
+
end
|
162
|
+
|
163
|
+
# Retrieves feature keys from campaign IDs
|
164
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
165
|
+
# @param campaign_ids [Array] The IDs of the campaigns
|
166
|
+
# @return [Array] The feature keys associated with the campaigns
|
167
|
+
def self.get_feature_keys_from_campaign_ids(settings, campaign_ids)
|
168
|
+
feature_keys = []
|
169
|
+
|
170
|
+
campaign_ids.each do |campaign|
|
171
|
+
campaign_id, variation_id = campaign.split('_').map(&:to_i)
|
172
|
+
|
173
|
+
settings.get_features.each do |feature|
|
174
|
+
next if feature_keys.include?(feature.get_key)
|
175
|
+
|
176
|
+
feature.get_rules.each do |rule|
|
177
|
+
if rule.get_campaign_id == campaign_id
|
178
|
+
feature_keys << feature.get_key if variation_id.nil? || rule.get_variation_id == variation_id
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
feature_keys
|
185
|
+
end
|
186
|
+
|
187
|
+
# Retrieves campaign IDs from a feature key
|
188
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
189
|
+
# @param feature_key [String] The key of the feature
|
190
|
+
# @return [Array] The campaign IDs associated with the feature
|
191
|
+
def self.get_campaign_ids_from_feature_key(settings, feature_key)
|
192
|
+
settings.get_features.each do |feature|
|
193
|
+
return feature.get_rules.map(&:get_campaign_id) if feature.get_key == feature_key
|
194
|
+
end
|
195
|
+
[]
|
196
|
+
end
|
197
|
+
|
198
|
+
# Assigns range values to a MEG campaign
|
199
|
+
# @param data [VariationModel] The variation to assign the start and end range values to
|
200
|
+
# @param current_allocation [Integer] The current allocation
|
201
|
+
# @return [Integer] The step factor
|
202
|
+
def self.assign_range_values_meg(data, current_allocation)
|
203
|
+
step_factor = get_variation_bucket_range(data.weight)
|
204
|
+
|
205
|
+
if step_factor > 0
|
206
|
+
data.start_range_variation = current_allocation + 1
|
207
|
+
data.end_range_variation = current_allocation + step_factor
|
208
|
+
else
|
209
|
+
data.start_range_variation = -1
|
210
|
+
data.end_range_variation = -1
|
211
|
+
end
|
212
|
+
|
213
|
+
step_factor
|
214
|
+
end
|
215
|
+
|
216
|
+
# Retrieves the rule type for a given campaign ID from a feature
|
217
|
+
def self.get_rule_type_using_campaign_id_from_feature(feature, campaign_id)
|
218
|
+
rule = feature.get_rules.find { |r| r.get_campaign_id == campaign_id }
|
219
|
+
rule ? rule.get_type : ''
|
220
|
+
end
|
221
|
+
|
222
|
+
# Calculates bucket range for a variation
|
223
|
+
# @param variation_weight [Float] The weight of the variation
|
224
|
+
# @return [Integer] The bucket range
|
225
|
+
def self.get_variation_bucket_range(variation_weight)
|
226
|
+
return 0 unless variation_weight && variation_weight.positive?
|
227
|
+
|
228
|
+
start_range = (variation_weight * 100).ceil
|
229
|
+
[start_range, Constants::MAX_TRAFFIC_VALUE].min
|
230
|
+
end
|
231
|
+
|
232
|
+
# Handles rollout campaign logic
|
233
|
+
# @param campaign [CampaignModel] The campaign to handle the rollout logic for
|
234
|
+
def self.handle_rollout_campaign(campaign)
|
235
|
+
campaign.get_variations.each do |variation|
|
236
|
+
end_range = variation.get_weight * 100
|
237
|
+
variation.set_start_range(1)
|
238
|
+
variation.set_end_range(end_range)
|
239
|
+
|
240
|
+
LoggerService.log(LogLevelEnum::INFO, "VARIATION_RANGE_ALLOCATION", {
|
241
|
+
variationKey: variation.get_key,
|
242
|
+
campaignKey: campaign.get_key,
|
243
|
+
variationWeight: variation.get_weight,
|
244
|
+
startRange: 1,
|
245
|
+
endRange: end_range
|
246
|
+
})
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Copyright 2025 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
|
+
module DataTypeUtil
|
16
|
+
# Checks if the value is a Hash (object in JS)
|
17
|
+
def self.is_object(val)
|
18
|
+
val.is_a?(Hash)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks if the value is an Array
|
22
|
+
def self.is_array(val)
|
23
|
+
val.is_a?(Array)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Checks if the value is nil
|
27
|
+
def self.is_null(val)
|
28
|
+
val.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
# Checks if the value is undefined (not applicable in Ruby, so return false)
|
32
|
+
def self.is_undefined(val)
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Checks if the value is defined (not nil)
|
37
|
+
def self.is_defined(val)
|
38
|
+
!val.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Checks if the value is a Number (including NaN)
|
42
|
+
def self.is_number(val)
|
43
|
+
val.is_a?(Numeric)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks if the value is a String
|
47
|
+
def self.is_string(val)
|
48
|
+
val.is_a?(String)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Checks if the value is a Boolean
|
52
|
+
def self.is_boolean(val)
|
53
|
+
val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Checks if the value is NaN (only applicable for Float in Ruby)
|
57
|
+
def self.is_nan(val)
|
58
|
+
val.is_a?(Float) && val.nan?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks if the value is a Date
|
62
|
+
def self.is_date(val)
|
63
|
+
val.is_a?(Date) || val.is_a?(Time) || val.is_a?(DateTime)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Checks if the value is a Function (Proc or Lambda in Ruby)
|
67
|
+
def self.is_function(val)
|
68
|
+
val.is_a?(Proc) || val.is_a?(Method)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks if the value is a Regular Expression
|
72
|
+
def self.is_regex(val)
|
73
|
+
val.is_a?(Regexp)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Determines the type of the given value
|
77
|
+
def self.get_type(val)
|
78
|
+
case
|
79
|
+
when is_object(val)
|
80
|
+
"Object"
|
81
|
+
when is_array(val)
|
82
|
+
"Array"
|
83
|
+
when is_null(val)
|
84
|
+
"Null"
|
85
|
+
when is_undefined(val)
|
86
|
+
"Undefined"
|
87
|
+
when is_nan(val)
|
88
|
+
"NaN"
|
89
|
+
when is_number(val)
|
90
|
+
"Number"
|
91
|
+
when is_string(val)
|
92
|
+
"String"
|
93
|
+
when is_boolean(val)
|
94
|
+
"Boolean"
|
95
|
+
when is_date(val)
|
96
|
+
"Date"
|
97
|
+
when is_regex(val)
|
98
|
+
"Regex"
|
99
|
+
when is_function(val)
|
100
|
+
"Function"
|
101
|
+
else
|
102
|
+
"Unknown Type"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|