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,253 @@
|
|
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/campaign_type_enum'
|
16
|
+
require_relative '../enums/status_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 '../models/user/context_model'
|
22
|
+
require_relative '../packages/decision_maker/decision_maker'
|
23
|
+
require_relative '../packages/segmentation_evaluator/core/segmentation_manager'
|
24
|
+
require_relative '../services/campaign_decision_service'
|
25
|
+
require_relative '../services/storage_service'
|
26
|
+
require_relative '../utils/data_type_util'
|
27
|
+
require_relative '../constants/constants'
|
28
|
+
require_relative '../utils/campaign_util'
|
29
|
+
require_relative '../utils/function_util'
|
30
|
+
require_relative '../utils/meg_util'
|
31
|
+
require_relative '../utils/uuid_util'
|
32
|
+
require_relative '../decorators/storage_decorator'
|
33
|
+
require_relative '../services/logger_service'
|
34
|
+
require_relative '../enums/log_level_enum'
|
35
|
+
|
36
|
+
class DecisionUtil
|
37
|
+
# Check if the campaign satisfies whitelisting and pre-segmentation
|
38
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
39
|
+
# @param feature [FeatureModel] The feature to evaluate
|
40
|
+
# @param campaign [CampaignModel] The campaign to evaluate
|
41
|
+
# @param context [ContextModel] The context for the evaluation
|
42
|
+
# @param evaluated_feature_map [Hash] The map of evaluated features
|
43
|
+
# @param meg_group_winner_campaigns [Hash] The map of MEG group winner campaigns
|
44
|
+
def self.check_whitelisting_and_pre_seg(settings, feature, campaign, context, evaluated_feature_map, meg_group_winner_campaigns, storage_service, decision)
|
45
|
+
vwo_user_id = UUIDUtil.get_uuid(context.get_id, settings.get_account_id)
|
46
|
+
campaign_id = campaign.get_id
|
47
|
+
|
48
|
+
if campaign.get_type == CampaignTypeEnum::AB
|
49
|
+
# Set _vwoUserId for variation targeting variables
|
50
|
+
variation_targeting_vars = context.get_variation_targeting_variables || {}
|
51
|
+
variation_targeting_vars["_vwoUserId"] = campaign.get_is_user_list_enabled ? vwo_user_id : context.get_id
|
52
|
+
context.set_variation_targeting_variables(variation_targeting_vars)
|
53
|
+
decision[:variation_targeting_variables] = variation_targeting_vars
|
54
|
+
|
55
|
+
# Check for whitelisting
|
56
|
+
if campaign.get_is_forced_variation_enabled
|
57
|
+
whitelisted_variation = check_campaign_whitelisting(campaign, context)
|
58
|
+
return [true, whitelisted_variation] if whitelisted_variation && !whitelisted_variation.empty?
|
59
|
+
else
|
60
|
+
LoggerService.log(LogLevelEnum::INFO, "WHITELISTING_SKIP", {
|
61
|
+
campaignKey: campaign.get_rule_key,
|
62
|
+
userId: context.get_id
|
63
|
+
})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# User list segment check for campaign pre-segmentation
|
68
|
+
custom_vars = context.get_custom_variables || {}
|
69
|
+
custom_vars["_vwoUserId"] = campaign.get_is_user_list_enabled ? vwo_user_id : context.get_id
|
70
|
+
context.set_custom_variables(custom_vars)
|
71
|
+
decision[:custom_variables] = custom_vars
|
72
|
+
|
73
|
+
# Check if rule belongs to Mutually Exclusive Group (MEG)
|
74
|
+
group_details = CampaignUtil.get_group_details_if_campaign_part_of_it(settings, campaign_id, campaign.get_type == CampaignTypeEnum::PERSONALIZE ? campaign.get_variations[0].get_id : nil)
|
75
|
+
group_id = group_details[:group_id]
|
76
|
+
|
77
|
+
# Check if the group has already been evaluated
|
78
|
+
group_winner_campaign_id = meg_group_winner_campaigns[group_id] if meg_group_winner_campaigns && meg_group_winner_campaigns.key?(group_id)
|
79
|
+
return evaluate_meg_campaign(group_winner_campaign_id, campaign, context, meg_group_winner_campaigns, group_id) if group_winner_campaign_id
|
80
|
+
|
81
|
+
# Check in storage if the group was already evaluated
|
82
|
+
stored_data = StorageDecorator.new.get_feature_from_storage("#{Constants::VWO_META_MEG_KEY}#{group_id}", context, storage_service)
|
83
|
+
if stored_data && stored_data[:experiment_key] && stored_data[:experiment_id]
|
84
|
+
LoggerService.log(LogLevelEnum::INFO, "MEG_CAMPAIGN_FOUND_IN_STORAGE", {
|
85
|
+
campaignKey: stored_data[:experiment_key],
|
86
|
+
userId: context.get_id
|
87
|
+
})
|
88
|
+
|
89
|
+
if stored_data[:experiment_id] == campaign_id
|
90
|
+
return evaluate_meg_personalization(campaign, stored_data, meg_group_winner_campaigns, group_id)
|
91
|
+
end
|
92
|
+
meg_group_winner_campaigns[group_id] = stored_data[:experiment_variation_id] != -1 ? "#{stored_data[:experiment_id]}_#{stored_data[:experiment_variation_id]}" : stored_data[:experiment_id]
|
93
|
+
return [false, nil]
|
94
|
+
end
|
95
|
+
|
96
|
+
# Pre-segmentation check
|
97
|
+
pre_segmentation_passed = CampaignDecisionService.new.get_pre_segmentation_decision(campaign, context)
|
98
|
+
|
99
|
+
if pre_segmentation_passed && group_id
|
100
|
+
winner_campaign = evaluate_groups(settings, feature, group_id, evaluated_feature_map, context, storage_service)
|
101
|
+
return evaluate_meg_campaign_winner(winner_campaign, campaign, context, meg_group_winner_campaigns, group_id)
|
102
|
+
end
|
103
|
+
|
104
|
+
[pre_segmentation_passed, nil]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Evaluate the MEG campaign
|
108
|
+
# @param group_winner_campaign_id [String] The ID of the MEG group winner campaign
|
109
|
+
# @param campaign [CampaignModel] The campaign to evaluate
|
110
|
+
# @param context [ContextModel] The context for the evaluation
|
111
|
+
# @param meg_group_winner_campaigns [Hash] The map of MEG group winner campaigns
|
112
|
+
# @param group_id [String] The ID of the MEG group
|
113
|
+
def self.evaluate_meg_campaign(group_winner_campaign_id, campaign, context, meg_group_winner_campaigns, group_id)
|
114
|
+
if campaign.get_type == CampaignTypeEnum::AB && group_winner_campaign_id == campaign.get_id
|
115
|
+
return [true, nil]
|
116
|
+
elsif campaign.get_type == CampaignTypeEnum::PERSONALIZE && group_winner_campaign_id == "#{campaign.get_id}_#{campaign.get_variations[0].get_id}"
|
117
|
+
return [true, nil]
|
118
|
+
end
|
119
|
+
[false, nil]
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.evaluate_meg_personalization(campaign, stored_data, meg_group_winner_campaigns, group_id)
|
123
|
+
if campaign.get_type == CampaignTypeEnum::PERSONALIZE
|
124
|
+
if stored_data[:experiment_variation_id] == campaign.get_variations[0].get_id
|
125
|
+
return [true, nil]
|
126
|
+
else
|
127
|
+
meg_group_winner_campaigns[group_id] = "#{stored_data[:experiment_id]}_#{stored_data[:experiment_variation_id]}"
|
128
|
+
return [false, nil]
|
129
|
+
end
|
130
|
+
else
|
131
|
+
return [true, nil]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Evaluate the MEG campaign winner
|
136
|
+
# @param winner_campaign [CampaignModel] The winner campaign
|
137
|
+
# @param campaign [CampaignModel] The campaign to evaluate
|
138
|
+
# @param context [ContextModel] The context for the evaluation
|
139
|
+
# @param meg_group_winner_campaigns [Hash] The map of MEG group winner campaigns
|
140
|
+
# @param group_id [String] The ID of the MEG group
|
141
|
+
def self.evaluate_meg_campaign_winner(winner_campaign, campaign, context, meg_group_winner_campaigns, group_id)
|
142
|
+
if winner_campaign && winner_campaign.get_id == campaign.get_id
|
143
|
+
if winner_campaign.get_type == CampaignTypeEnum::AB
|
144
|
+
return [true, nil]
|
145
|
+
else
|
146
|
+
# if personalise then check if the requested variation is the winner
|
147
|
+
if winner_campaign.get_variations[0].get_id == campaign.get_variations[0].get_id
|
148
|
+
return [true, nil]
|
149
|
+
else
|
150
|
+
meg_group_winner_campaigns[group_id] = "#{winner_campaign.get_id}_#{winner_campaign.get_variations[0].get_id}"
|
151
|
+
return [false, nil]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
elsif winner_campaign
|
155
|
+
if winner_campaign.get_type == CampaignTypeEnum::AB
|
156
|
+
meg_group_winner_campaigns[group_id] = winner_campaign.get_id
|
157
|
+
else
|
158
|
+
meg_group_winner_campaigns[group_id] = "#{winner_campaign.get_id}_#{winner_campaign.get_variations[0].get_id}"
|
159
|
+
end
|
160
|
+
return [false, nil]
|
161
|
+
end
|
162
|
+
|
163
|
+
meg_group_winner_campaigns[group_id] = -1
|
164
|
+
[false, nil]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Evaluate the traffic and get the variation
|
168
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
169
|
+
# @param campaign [CampaignModel] The campaign to evaluate
|
170
|
+
# @param user_id [String] The ID of the user
|
171
|
+
def self.evaluate_traffic_and_get_variation(settings, campaign, user_id)
|
172
|
+
variation = CampaignDecisionService.new.get_variation_alloted(user_id, settings.get_account_id, campaign)
|
173
|
+
if variation.nil?
|
174
|
+
LoggerService.log(LogLevelEnum::INFO, "USER_CAMPAIGN_BUCKET_INFO", {
|
175
|
+
campaignKey: campaign.get_type == CampaignTypeEnum::AB ? campaign.get_key : "#{campaign.get_name}_#{campaign.get_rule_key}",
|
176
|
+
userId: user_id,
|
177
|
+
status: 'did not get any variation'
|
178
|
+
})
|
179
|
+
return nil
|
180
|
+
end
|
181
|
+
LoggerService.log(LogLevelEnum::INFO, "USER_CAMPAIGN_BUCKET_INFO", {
|
182
|
+
campaignKey: campaign.get_type == CampaignTypeEnum::AB ? campaign.get_key : "#{campaign.get_name}_#{campaign.get_rule_key}",
|
183
|
+
userId: user_id,
|
184
|
+
status: "got variation: #{variation.get_key}"
|
185
|
+
})
|
186
|
+
variation
|
187
|
+
end
|
188
|
+
|
189
|
+
# Check if the campaign satisfies whitelisting
|
190
|
+
# @param campaign [CampaignModel] The campaign to evaluate
|
191
|
+
# @param context [ContextModel] The context for the evaluation
|
192
|
+
def self.check_campaign_whitelisting(campaign, context)
|
193
|
+
# Check if the campaign satisfies whitelisting
|
194
|
+
whitelisting_result = evaluate_whitelisting(campaign, context)
|
195
|
+
status = whitelisting_result ? StatusEnum::PASSED : StatusEnum::FAILED
|
196
|
+
variation_string = whitelisting_result ? whitelisting_result[:variation].get_key : ''
|
197
|
+
|
198
|
+
LoggerService.log(LogLevelEnum::INFO, "WHITELISTING_STATUS", {
|
199
|
+
userId: context.get_id,
|
200
|
+
campaignKey: campaign.get_type == CampaignTypeEnum::AB ? campaign.get_key : "#{campaign.get_name}_#{campaign.get_rule_key}",
|
201
|
+
status: status,
|
202
|
+
variationString: variation_string
|
203
|
+
})
|
204
|
+
|
205
|
+
whitelisting_result
|
206
|
+
end
|
207
|
+
|
208
|
+
# Evaluate the whitelisting
|
209
|
+
# @param campaign [CampaignModel] The campaign to evaluate
|
210
|
+
# @param context [ContextModel] The context for the evaluation
|
211
|
+
def self.evaluate_whitelisting(campaign, context)
|
212
|
+
targeted_variations = []
|
213
|
+
|
214
|
+
campaign.get_variations.each do |variation|
|
215
|
+
next if DataTypeUtil.is_object(variation.get_segments) && variation.get_segments.empty?
|
216
|
+
if DataTypeUtil.is_object(variation.get_segments)
|
217
|
+
segmentation_result = SegmentationManager.instance.validate_segmentation(
|
218
|
+
variation.get_segments,
|
219
|
+
context.get_variation_targeting_variables
|
220
|
+
)
|
221
|
+
|
222
|
+
targeted_variations.push(clone_object(variation)) if segmentation_result
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Determine the whitelisted variation
|
227
|
+
whitelisted_variation = nil
|
228
|
+
if targeted_variations.length > 1
|
229
|
+
scale_variation_weights(targeted_variations)
|
230
|
+
current_allocation = 0
|
231
|
+
|
232
|
+
targeted_variations.each do |variation|
|
233
|
+
step_factor = assign_range_values(variation, current_allocation)
|
234
|
+
current_allocation += step_factor
|
235
|
+
end
|
236
|
+
|
237
|
+
whitelisted_variation = CampaignDecisionService.new.get_variation(
|
238
|
+
targeted_variations,
|
239
|
+
DecisionMaker.new.calculate_bucket_value(CampaignUtil.get_bucketing_seed(context.get_id, campaign, nil))
|
240
|
+
)
|
241
|
+
else
|
242
|
+
whitelisted_variation = targeted_variations.first
|
243
|
+
end
|
244
|
+
|
245
|
+
return nil unless whitelisted_variation
|
246
|
+
|
247
|
+
{
|
248
|
+
variation: whitelisted_variation,
|
249
|
+
variation_name: whitelisted_variation.get_key,
|
250
|
+
variation_id: whitelisted_variation.get_id
|
251
|
+
}
|
252
|
+
end
|
253
|
+
end
|
@@ -0,0 +1,123 @@
|
|
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/campaign_type_enum'
|
16
|
+
require_relative '../models/campaign/campaign_model'
|
17
|
+
require_relative '../models/campaign/feature_model'
|
18
|
+
require_relative '../models/settings/settings_model'
|
19
|
+
require_relative '../utils/data_type_util'
|
20
|
+
|
21
|
+
# Clones an object deeply.
|
22
|
+
# @param obj [Object] The object to clone.
|
23
|
+
# @return [Object] The cloned object.
|
24
|
+
def clone_object(obj)
|
25
|
+
return obj unless obj
|
26
|
+
Marshal.load(Marshal.dump(obj))
|
27
|
+
end
|
28
|
+
|
29
|
+
# Gets the current time in ISO string format.
|
30
|
+
# @return [String] The current time in ISO string format.
|
31
|
+
def get_current_time
|
32
|
+
Time.now.utc.iso8601
|
33
|
+
end
|
34
|
+
|
35
|
+
# Gets the current Unix timestamp in seconds.
|
36
|
+
# @return [Integer] The current Unix timestamp.
|
37
|
+
def get_current_unix_timestamp
|
38
|
+
Time.now.to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gets the current Unix timestamp in milliseconds.
|
42
|
+
# @return [Integer] The current Unix timestamp in milliseconds.
|
43
|
+
def get_current_unix_timestamp_in_millis
|
44
|
+
(Time.now.to_f * 1000).to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
# Generates a random number between 0 and 1.
|
48
|
+
# @return [Float] A random number.
|
49
|
+
def get_random_number
|
50
|
+
rand
|
51
|
+
end
|
52
|
+
|
53
|
+
# Retrieves specific rules based on the type from a feature.
|
54
|
+
# @param feature [FeatureModel] The feature object.
|
55
|
+
# @param type [CampaignTypeEnum, nil] The type of the rules to retrieve.
|
56
|
+
# @return [Array] An array of rules that match the type.
|
57
|
+
def get_specific_rules_based_on_type(feature, type = nil)
|
58
|
+
return [] unless feature&.get_rules_linked_campaign
|
59
|
+
|
60
|
+
return feature.get_rules_linked_campaign if type.nil? || !DataTypeUtil.is_string(type)
|
61
|
+
|
62
|
+
feature.get_rules_linked_campaign.select do |rule|
|
63
|
+
rule_model = CampaignModel.new.model_from_dictionary(rule)
|
64
|
+
rule_model.get_type == type
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Retrieves all AB and Personalize rules from a feature.
|
69
|
+
# @param feature [FeatureModel] The feature object.
|
70
|
+
# @return [Array] An array of AB and Personalize rules.
|
71
|
+
def get_all_experiment_rules(feature)
|
72
|
+
return [] unless feature
|
73
|
+
|
74
|
+
feature.get_rules_linked_campaign.select do |rule|
|
75
|
+
[CampaignTypeEnum::AB, CampaignTypeEnum::PERSONALIZE].include?(rule.get_type)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Retrieves a feature by its key from the settings.
|
80
|
+
# @param settings [SettingsModel] The settings containing features.
|
81
|
+
# @param feature_key [String] The key of the feature to find.
|
82
|
+
# @return [FeatureModel, nil] The feature if found, otherwise nil.
|
83
|
+
def get_feature_from_key(settings, feature_key)
|
84
|
+
return nil unless settings&.get_features
|
85
|
+
|
86
|
+
settings.get_features.find { |feature| feature.get_key == feature_key }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Checks if an event exists within any feature's metrics.
|
90
|
+
# @param event_name [String] The name of the event to check.
|
91
|
+
# @param settings [SettingsModel] The settings containing features.
|
92
|
+
# @return [Boolean] True if the event exists, otherwise false.
|
93
|
+
def does_event_belong_to_any_feature(event_name, settings)
|
94
|
+
settings.get_features.any? do |feature|
|
95
|
+
feature.get_metrics.any? { |metric| metric.get_identifier == event_name }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Adds linked campaigns to each feature in the settings based on rules.
|
100
|
+
# @param settings [SettingsModel] The settings file to modify.
|
101
|
+
def add_linked_campaigns_to_settings(settings)
|
102
|
+
campaign_map = settings.get_campaigns.each_with_object({}) do |campaign, map|
|
103
|
+
map[campaign.get_id] = campaign
|
104
|
+
end
|
105
|
+
|
106
|
+
settings.get_features.each do |feature|
|
107
|
+
rules_linked_campaign = feature.get_rules.map do |rule|
|
108
|
+
campaign = campaign_map[rule.get_campaign_id]
|
109
|
+
next unless campaign
|
110
|
+
|
111
|
+
linked_campaign = campaign.clone
|
112
|
+
linked_campaign.set_rule_key(rule.get_rule_key)
|
113
|
+
if rule.get_variation_id
|
114
|
+
variation = campaign.get_variations.find { |v| v.get_id == rule.get_variation_id }
|
115
|
+
linked_campaign.set_variations([variation]) if variation
|
116
|
+
end
|
117
|
+
|
118
|
+
linked_campaign
|
119
|
+
end.compact
|
120
|
+
|
121
|
+
feature.set_rules_linked_campaign(rules_linked_campaign)
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,101 @@
|
|
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 '../models/settings/settings_model'
|
16
|
+
require_relative '../packages/network_layer/manager/network_manager'
|
17
|
+
require_relative '../packages/network_layer/models/request_model'
|
18
|
+
require_relative '../enums/http_method_enum'
|
19
|
+
require_relative '../services/settings_service'
|
20
|
+
require_relative 'url_util'
|
21
|
+
require_relative '../enums/campaign_type_enum'
|
22
|
+
require_relative '../services/logger_service'
|
23
|
+
require_relative '../enums/log_level_enum'
|
24
|
+
|
25
|
+
# Retrieves data from a web service using the specified query parameters and endpoint.
|
26
|
+
# @param query_params [Hash] The parameters to be used in the query string of the request.
|
27
|
+
# @param endpoint [String] The endpoint URL to which the request is sent.
|
28
|
+
# @return [Hash, Boolean] The response data or false if an error occurs.
|
29
|
+
def get_from_gateway_service(query_params, endpoint)
|
30
|
+
network_instance = NetworkManager.instance
|
31
|
+
|
32
|
+
unless SettingsService.instance.is_gateway_service_provided
|
33
|
+
LoggerService.log(LogLevelEnum::ERROR, "GATEWAY_URL_ERROR")
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
request = RequestModel.new(
|
39
|
+
UrlUtil.get_base_url,
|
40
|
+
HttpMethodEnum::GET,
|
41
|
+
endpoint,
|
42
|
+
query_params,
|
43
|
+
nil,
|
44
|
+
nil,
|
45
|
+
SettingsService.instance.protocol,
|
46
|
+
SettingsService.instance.port
|
47
|
+
)
|
48
|
+
|
49
|
+
response = network_instance.get(request)
|
50
|
+
return response.get_data
|
51
|
+
rescue StandardError => e
|
52
|
+
LoggerService.log(LogLevelEnum::ERROR, "Error fetching from Gateway Service: #{e.message}", nil)
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Encodes the query parameters to ensure they are URL-safe.
|
58
|
+
# @param query_params [Hash] The query parameters to be encoded.
|
59
|
+
# @return [Hash] An object containing the encoded query parameters.
|
60
|
+
def get_query_params(query_params)
|
61
|
+
encoded_params = {}
|
62
|
+
|
63
|
+
query_params.each do |key, value|
|
64
|
+
encoded_params[key] = URI.encode_www_form_component(value.to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
encoded_params
|
68
|
+
end
|
69
|
+
|
70
|
+
# Adds isGatewayServiceRequired flag to each feature in the settings based on pre-segmentation.
|
71
|
+
# @param settings [SettingsModel] The settings file to modify.
|
72
|
+
def add_is_gateway_service_required_flag(settings)
|
73
|
+
pattern = /
|
74
|
+
(?!custom_variable\s*:\s*{\s*"name"\s*:\s*") # Prevent matching inside custom_variable
|
75
|
+
\b(country|region|city|os|device|device_type|browser_string|ua)\b
|
76
|
+
|
|
77
|
+
"custom_variable"\s*:\s*{\s*"name"\s*:\s*"inlist\([^)]*\)"
|
78
|
+
/x
|
79
|
+
|
80
|
+
settings.get_features.each do |feature|
|
81
|
+
feature.get_rules_linked_campaign.each do |rule|
|
82
|
+
segments = {}
|
83
|
+
|
84
|
+
if [CampaignTypeEnum::PERSONALIZE, CampaignTypeEnum::ROLLOUT].include?(rule.get_type)
|
85
|
+
segments = rule.get_variations[0].get_segments
|
86
|
+
else
|
87
|
+
segments = rule.get_segments
|
88
|
+
end
|
89
|
+
|
90
|
+
next unless segments
|
91
|
+
|
92
|
+
json_segments = segments.to_json
|
93
|
+
matches = json_segments.scan(pattern)
|
94
|
+
|
95
|
+
if matches.any?
|
96
|
+
feature.set_is_gateway_service_required(true)
|
97
|
+
break
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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 '../models/settings/settings_model'
|
16
|
+
require_relative './network_util'
|
17
|
+
require_relative '../models/user/context_model'
|
18
|
+
require_relative '../enums/event_enum'
|
19
|
+
|
20
|
+
# Creates and sends an impression for a variation shown event.
|
21
|
+
# This function constructs the necessary properties and payload for the event
|
22
|
+
# and uses the NetworkUtil to send a POST API request.
|
23
|
+
#
|
24
|
+
# @param settings [SettingsModel] The settings model containing configuration.
|
25
|
+
# @param campaign_id [Integer] The ID of the campaign.
|
26
|
+
# @param variation_id [Integer] The ID of the variation shown to the user.
|
27
|
+
# @param context [ContextModel] The user context model containing user-specific data.
|
28
|
+
def create_and_send_impression_for_variation_shown(settings, campaign_id, variation_id, context)
|
29
|
+
# Get base properties for the event
|
30
|
+
properties = NetworkUtil.get_events_base_properties(
|
31
|
+
settings,
|
32
|
+
EventEnum::VWO_VARIATION_SHOWN,
|
33
|
+
URI.encode_www_form_component(context.get_user_agent), # Encode user agent for URL safety
|
34
|
+
context.get_ip_address
|
35
|
+
)
|
36
|
+
|
37
|
+
# Construct payload data for tracking the user
|
38
|
+
payload = NetworkUtil.get_track_user_payload_data(
|
39
|
+
settings,
|
40
|
+
context.get_id,
|
41
|
+
EventEnum::VWO_VARIATION_SHOWN,
|
42
|
+
campaign_id,
|
43
|
+
variation_id,
|
44
|
+
context.get_user_agent,
|
45
|
+
context.get_ip_address
|
46
|
+
)
|
47
|
+
|
48
|
+
NetworkUtil.send_post_api_request(properties, payload)
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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
|
+
# Constructs a message by replacing placeholders in a template with corresponding values from a data hash.
|
16
|
+
#
|
17
|
+
# @param template [String] The message template containing placeholders in the format `{key}`.
|
18
|
+
# @param data [Hash] An object containing keys and values used to replace the placeholders in the template.
|
19
|
+
# @return [String] The constructed message with all placeholders replaced by their corresponding values from the data hash.
|
20
|
+
def build_message(template, data = {})
|
21
|
+
begin
|
22
|
+
template.gsub(/\{([0-9a-zA-Z_]+)\}/) do |match|
|
23
|
+
key = match.tr('{}', '') # Extract the key from `{key}`
|
24
|
+
|
25
|
+
# Check for escaped placeholders like `{{key}}`
|
26
|
+
if template[template.index(match) - 1] == '{' && template[template.index(match) + match.length] == '}'
|
27
|
+
key
|
28
|
+
else
|
29
|
+
value = data[key.to_sym] || data[key] # Support both string and symbol keys
|
30
|
+
|
31
|
+
# Return empty string if key is missing
|
32
|
+
next '' if value.nil?
|
33
|
+
|
34
|
+
# If value is a callable (Proc/Lambda), execute it
|
35
|
+
value.respond_to?(:call) ? value.call : value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue StandardError
|
39
|
+
template # Return the original template in case of an error
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|