vwo-fme-ruby-sdk 1.0.0 → 1.2.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/resources/debug_messages.json +13 -0
- data/lib/resources/error_messages.json +30 -0
- data/lib/resources/info_messages.json +33 -0
- data/lib/resources/warn_messages.json +1 -0
- 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 +191 -9
- data/lib/vwo/utils/request.rb +0 -89
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e0d70f7fcb19c20548cdb55639655c0098f5fa149137014982baafb8bd185e9
|
4
|
+
data.tar.gz: 54c0eab76aef36e5de32fc4953e3cea7add5bbed100d1777c08da18cff900b01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e78ca66f6fb6c71e60519658e04362c0b701d81ac3c79ecd18b8ad7aafedce4aec619dac425f1060b4c3f23aec55d43844645be69f998c4a280cd9a0ee70a9b7
|
7
|
+
data.tar.gz: 543ed4464c0e6685a6fa695d5237a573f0553f29f39bd13476451f2ba6e2a4ea21bfe595c01b1aceb356c9ca159e1c739d2f9e33f2eefc3ce9afcb70283c04ab
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"API_CALLED": "API - {apiName} called",
|
3
|
+
"SERVICE_INITIALIZED": "VWO {service} initialized while creating an instance of SDK",
|
4
|
+
|
5
|
+
"EXPERIMENTS_EVALUATION_WHEN_ROLLOUT_PASSED": "Rollout rule got passed for user {userId}. Hence, evaluating experiments",
|
6
|
+
"EXPERIMENTS_EVALUATION_WHEN_NO_ROLLOUT_PRESENT": "No Rollout rules present for the feature. Hence, checking experiment rules",
|
7
|
+
|
8
|
+
"USER_BUCKET_TO_VARIATION": "User ID:{userId} for experiment:{campaignKey} having percent traffic:{percentTraffic} got bucket-value:{bucketValue} and hash-value:{hashValue}",
|
9
|
+
|
10
|
+
"IMPRESSION_FOR_TRACK_USER": "Impression built for vwo_variationShown(VWO standard event for tracking user) event haivng Account ID:{accountId}, User ID:{userId}, and experiment ID:{campaignId}",
|
11
|
+
"IMPRESSION_FOR_TRACK_GOAL": "Impression built for event:{eventName} event having Account ID:{accountId}, and user ID:{userId}",
|
12
|
+
"IMPRESSION_FOR_SYNC_VISITOR_PROP": "Impression built for {eventName}(VWO internal event) event for Account ID:{accountId}, and user ID:{userId}"
|
13
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"API_THROW_ERROR": "API - {apiName} failed to execute. Trace:{err}",
|
3
|
+
"INIT_OPTIONS_ERROR": "[ERROR]: VWO-SDK {date} Options should be of type object",
|
4
|
+
"INIT_OPTIONS_SDK_KEY_ERROR": "[ERROR]: VWO-SDK {date} Please provide the sdkKey in the options and should be a of type string",
|
5
|
+
"INIT_OPTIONS_ACCOUNT_ID_ERROR": "[ERROR]: VWO-SDK {date} Please provide VWO account ID in the options and should be a of type string|number",
|
6
|
+
|
7
|
+
"INIT_OPTIONS_INVALID": "Invalid key:{key} passed in options. Should be of type:{correctType} and greater than equal to 1000",
|
8
|
+
|
9
|
+
"SETTINGS_FETCH_ERROR": "Settings could not be fetched. Error:{err}",
|
10
|
+
"SETTINGS_SCHEMA_INVALID": "Settings are not valid. Failed schema validation",
|
11
|
+
|
12
|
+
"POLLING_FETCH_SETTINGS_FAILED": "Error while fetching VWO settings with polling",
|
13
|
+
|
14
|
+
"API_INVALID_PARAM": "Key:{key} passed to API:{apiName} is not of valid type. Got type:{type}, should be:{correctType}",
|
15
|
+
"API_SETTING_INVALID": "Settings are not valid. Contact VWO Support",
|
16
|
+
"API_CONTEXT_INVALID": "Context should be an object and must contain a mandatory key - id, which is User ID",
|
17
|
+
|
18
|
+
"FEATURE_NOT_FOUND": "Feature not found for the key:{featureKey}",
|
19
|
+
"EVENT_NOT_FOUND": "Event:{eventName} not found in any of the features' metrics",
|
20
|
+
|
21
|
+
"STORED_DATA_ERROR": "Error in getting data from storage. Error:{err}",
|
22
|
+
"STORING_DATA_ERROR": "Key:{featureKey} is not valid. Not able to store data into storage",
|
23
|
+
|
24
|
+
"GATEWAY_URL_ERROR": "Please provide a valid URL for VWO Gateway Service while initializing the SDK",
|
25
|
+
|
26
|
+
"NETWORK_CALL_FAILED": "Error occurred while sending {method} request. Error:{err}",
|
27
|
+
"SETTINGS_FETCH_FAILED": "Failed to fetch settings and hence VWO client instance couldn't be updated when API: {apiName} got called having isViaWebhook param as {isViaWebhook}. Error: {err}",
|
28
|
+
"NETWORK_CALL_RETRY_ATTEMPT": "Request failed for {endPoint}, Error: {err}. Retrying in {delay} seconds, attempt {attempt} of {maxRetries}",
|
29
|
+
"NETWORK_CALL_RETRY_FAILED": "Max retries reached. Request failed for {endPoint}, Error: {err}"
|
30
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"ON_INIT_ALREADY_RESOLVED": "[INFO]: VWO-SDK {date} {apiName} already resolved",
|
3
|
+
"ON_INIT_SETTINGS_FAILED": "[INFO]: VWO-SDK {date} VWO settings could not be fetched",
|
4
|
+
|
5
|
+
"POLLING_SET_SETTINGS": "There's a change in settings from the last settings fetched. Hence, instantiating a new VWO client internally",
|
6
|
+
"POLLING_NO_CHANGE_IN_SETTINGS": "No change in settings with the last settings fetched. Hence, not instantiating new VWO client",
|
7
|
+
|
8
|
+
"SETTINGS_FETCH_SUCCESS": "Settings fetched successfully",
|
9
|
+
|
10
|
+
"CLIENT_INITIALIZED": "VWO Client initialized",
|
11
|
+
|
12
|
+
"STORED_VARIATION_FOUND": "Variation {variationKey} found in storage for the user {userId} for the {experimentType} experiment:{experimentKey}",
|
13
|
+
|
14
|
+
"USER_PART_OF_CAMPAIGN": "User ID:{userId} is {notPart} part of experiment:{campaignKey}",
|
15
|
+
"SEGMENTATION_SKIP": "For userId:{userId} of experiment:{campaignKey}, segments was missing. Hence, skipping segmentation",
|
16
|
+
"SEGMENTATION_STATUS": "Segmentation {status} for userId:{userId} of experiment:{campaignKey}",
|
17
|
+
|
18
|
+
"USER_CAMPAIGN_BUCKET_INFO": "User ID:{userId} for experiment:{campaignKey} {status}",
|
19
|
+
|
20
|
+
"WHITELISTING_SKIP": "Whitelisting is not used for experiment:{campaignKey}, hence skipping evaluating whitelisting {variation} for User ID:{userId}",
|
21
|
+
"WHITELISTING_STATUS": "User ID:{userId} for experiment:{campaignKey} {status} whitelisting {variationString}",
|
22
|
+
|
23
|
+
"VARIATION_RANGE_ALLOCATION": "Variation:{variationKey} of experiment:{campaignKey} having weight:{variationWeight} got bucketing range: ({startRange} - {endRange})",
|
24
|
+
|
25
|
+
"IMPACT_ANALYSIS": "Tracking feature:{featureKey} being {status} for Impact Analysis Campaign for the user {userId}",
|
26
|
+
|
27
|
+
"MEG_SKIP_ROLLOUT_EVALUATE_EXPERIMENTS": "No rollout rule found for feature:{featureKey}. Hence, evaluating experiments",
|
28
|
+
"MEG_CAMPAIGN_FOUND_IN_STORAGE": "Campaign {campaignKey} found in storage for user ID:{userId}",
|
29
|
+
"MEG_CAMPAIGN_ELIGIBLE": "Campaign {campaignKey} is eligible for user ID:{userId}",
|
30
|
+
"MEG_WINNER_CAMPAIGN": "MEG: Campaign {campaignKey} is the winner for group {groupId} for user ID:{userId} {algo}",
|
31
|
+
"SETTINGS_UPDATED": "Settings fetched and updated successfully on the current VWO client instance when API: {apiName} got called having isViaWebhook param as {isViaWebhook}",
|
32
|
+
"NETWORK_CALL_SUCCESS": "Impression for {event} - {endPoint} was successfully received by VWO having Account ID:{accountId}, User ID:{userId} and UUID: {uuid}"
|
33
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{}
|
@@ -0,0 +1,236 @@
|
|
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/api_enum'
|
16
|
+
require_relative '../enums/campaign_type_enum'
|
17
|
+
require_relative '../enums/decision_types_enum'
|
18
|
+
require_relative '../decorators/storage_decorator'
|
19
|
+
require_relative '../services/storage_service'
|
20
|
+
require_relative '../models/campaign/feature_model'
|
21
|
+
require_relative '../models/campaign/campaign_model'
|
22
|
+
require_relative '../packages/segmentation_evaluator/core/segmentation_manager'
|
23
|
+
require_relative '../services/logger_service'
|
24
|
+
require_relative '../utils/campaign_util'
|
25
|
+
require_relative '../utils/rule_evaluation_util'
|
26
|
+
require_relative '../utils/impression_util'
|
27
|
+
require_relative '../utils/decision_util'
|
28
|
+
require_relative '../models/user/get_flag_response'
|
29
|
+
|
30
|
+
class FlagApi
|
31
|
+
# Get the flag for a given feature key and context
|
32
|
+
# @param feature_key [String] The key of the feature to get the flag for
|
33
|
+
# @param settings [SettingsModel] The settings for the VWO instance
|
34
|
+
# @param context [ContextModel] The context for the evaluation
|
35
|
+
# @param hooks_service [HooksService] The hooks service for the VWO instance
|
36
|
+
# @return [GetFlagResponse] The flag for the given feature key and context
|
37
|
+
def get(feature_key, settings, context, hooks_service)
|
38
|
+
is_enabled = false
|
39
|
+
rollout_variation_to_return = nil
|
40
|
+
experiment_variation_to_return = nil
|
41
|
+
should_check_for_experiments_rules = false
|
42
|
+
|
43
|
+
passed_rules_information = {} # for storing integration callback
|
44
|
+
evaluated_feature_map = {}
|
45
|
+
|
46
|
+
# Fetch feature object using the feature key
|
47
|
+
feature = get_feature_from_key(settings, feature_key)
|
48
|
+
|
49
|
+
decision = {
|
50
|
+
feature_name: feature&.get_name,
|
51
|
+
feature_id: feature&.get_id,
|
52
|
+
feature_key: feature&.get_key,
|
53
|
+
user_id: context&.get_id,
|
54
|
+
api: ApiEnum::GET_FLAG
|
55
|
+
}
|
56
|
+
|
57
|
+
storage_service = StorageService.new
|
58
|
+
stored_data = StorageDecorator.new.get_feature_from_storage(feature_key, context, storage_service)
|
59
|
+
|
60
|
+
if stored_data && stored_data[:experiment_variation_id]
|
61
|
+
if stored_data[:experiment_key]
|
62
|
+
variation = CampaignUtil.get_variation_from_campaign_key(settings, stored_data[:experiment_key], stored_data[:experiment_variation_id])
|
63
|
+
|
64
|
+
if variation
|
65
|
+
LoggerService.log(LogLevelEnum::INFO, "STORED_VARIATION_FOUND", {variationKey: variation.get_key, userId: context.get_id, experimentKey: stored_data[:experiment_key], experimentType: "experiment"})
|
66
|
+
return GetFlagResponse.new(true, variation.get_variables)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
elsif stored_data && stored_data[:rollout_key] && stored_data[:rollout_id]
|
70
|
+
variation = CampaignUtil.get_variation_from_campaign_key(settings, stored_data[:rollout_key], stored_data[:rollout_variation_id])
|
71
|
+
|
72
|
+
if variation
|
73
|
+
LoggerService.log(LogLevelEnum::INFO, "STORED_VARIATION_FOUND", {variationKey: variation.get_key, userId: context.get_id, experimentKey: stored_data[:rollout_key], experimentType: "rollout"})
|
74
|
+
LoggerService.log(LogLevelEnum::DEBUG, "EXPERIMENTS_EVALUATION_WHEN_ROLLOUT_PASSED", {userId: context.get_id})
|
75
|
+
|
76
|
+
is_enabled = true
|
77
|
+
should_check_for_experiments_rules = true
|
78
|
+
rollout_variation_to_return = variation
|
79
|
+
feature_info = {
|
80
|
+
rollout_id: stored_data[:rollout_id],
|
81
|
+
rollout_key: stored_data[:rollout_key],
|
82
|
+
rollout_variation_id: stored_data[:rollout_variation_id]
|
83
|
+
}
|
84
|
+
evaluated_feature_map[feature_key] = feature_info
|
85
|
+
passed_rules_information.merge!(feature_info)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if feature.nil?
|
90
|
+
LoggerService.log(LogLevelEnum::ERROR, "FEATURE_NOT_FOUND", {featureKey: feature_key})
|
91
|
+
return GetFlagResponse.new(false, [])
|
92
|
+
end
|
93
|
+
|
94
|
+
# Segmentation evaluation
|
95
|
+
SegmentationManager.instance.set_contextual_data(settings, feature, context)
|
96
|
+
|
97
|
+
# Get all rollout rules
|
98
|
+
rollout_rules = get_specific_rules_based_on_type(feature, CampaignTypeEnum::ROLLOUT)
|
99
|
+
|
100
|
+
if rollout_rules.any? && !is_enabled
|
101
|
+
rollout_rules_to_evaluate = []
|
102
|
+
|
103
|
+
rollout_rules.each do |rule|
|
104
|
+
result = evaluate_rule(settings, feature, rule, context, evaluated_feature_map, nil, storage_service, decision)
|
105
|
+
pre_segmentation_result = result[:pre_segmentation_result]
|
106
|
+
updated_decision = result[:updated_decision]
|
107
|
+
decision.merge!(updated_decision) if updated_decision
|
108
|
+
|
109
|
+
if pre_segmentation_result
|
110
|
+
rollout_rules_to_evaluate << rule
|
111
|
+
evaluated_feature_map[feature_key] = {
|
112
|
+
rollout_id: rule.get_id,
|
113
|
+
rollout_key: rule.get_key,
|
114
|
+
rollout_variation_id: rule.get_variations.first&.get_id
|
115
|
+
}
|
116
|
+
break
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
if rollout_rules_to_evaluate.any?
|
121
|
+
passed_rollout_campaign = CampaignModel.new.model_from_dictionary(rollout_rules_to_evaluate.first)
|
122
|
+
variation = DecisionUtil.evaluate_traffic_and_get_variation(settings, passed_rollout_campaign, context.get_id)
|
123
|
+
|
124
|
+
if variation
|
125
|
+
is_enabled = true
|
126
|
+
should_check_for_experiments_rules = true
|
127
|
+
rollout_variation_to_return = variation
|
128
|
+
update_integrations_decision_object(passed_rollout_campaign, variation, passed_rules_information, decision)
|
129
|
+
|
130
|
+
create_and_send_impression_for_variation_shown(settings, passed_rollout_campaign.get_id, variation.get_id, context)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
elsif rollout_rules.empty?
|
134
|
+
LoggerService.log(LogLevelEnum::DEBUG, "EXPERIMENTS_EVALUATION_WHEN_NO_ROLLOUT_PRESENT")
|
135
|
+
should_check_for_experiments_rules = true
|
136
|
+
end
|
137
|
+
|
138
|
+
if should_check_for_experiments_rules
|
139
|
+
experiment_rules = get_all_experiment_rules(feature)
|
140
|
+
experiment_rules_to_evaluate = []
|
141
|
+
meg_group_winner_campaigns = {}
|
142
|
+
|
143
|
+
experiment_rules.each do |rule|
|
144
|
+
result = evaluate_rule(settings, feature, rule, context, evaluated_feature_map, meg_group_winner_campaigns, storage_service, decision)
|
145
|
+
pre_segmentation_result = result[:pre_segmentation_result]
|
146
|
+
whitelisted_object = result[:whitelisted_object]
|
147
|
+
updated_decision = result[:updated_decision]
|
148
|
+
decision.merge!(updated_decision) if updated_decision
|
149
|
+
|
150
|
+
if pre_segmentation_result
|
151
|
+
if whitelisted_object.nil?
|
152
|
+
experiment_rules_to_evaluate << rule
|
153
|
+
else
|
154
|
+
is_enabled = true
|
155
|
+
experiment_variation_to_return = whitelisted_object[:variation]
|
156
|
+
passed_rules_information.merge!(
|
157
|
+
experiment_id: rule.get_id,
|
158
|
+
experiment_key: rule.get_key,
|
159
|
+
experiment_variation_id: whitelisted_object[:variation_id]
|
160
|
+
)
|
161
|
+
end
|
162
|
+
break
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
if experiment_rules_to_evaluate.any?
|
167
|
+
campaign = CampaignModel.new.model_from_dictionary(experiment_rules_to_evaluate.first)
|
168
|
+
variation = DecisionUtil.evaluate_traffic_and_get_variation(settings, campaign, context.get_id)
|
169
|
+
|
170
|
+
if variation
|
171
|
+
is_enabled = true
|
172
|
+
experiment_variation_to_return = variation
|
173
|
+
update_integrations_decision_object(campaign, variation, passed_rules_information, decision)
|
174
|
+
|
175
|
+
create_and_send_impression_for_variation_shown(settings, campaign.get_id, variation.get_id, context)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Store evaluated feature in storage
|
181
|
+
if is_enabled
|
182
|
+
StorageDecorator.new.set_data_in_storage(
|
183
|
+
{ feature_key: feature_key, context: context }.merge(passed_rules_information),
|
184
|
+
storage_service
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Execute hooks
|
189
|
+
hooks_service.set(decision)
|
190
|
+
hooks_service.execute(hooks_service.get)
|
191
|
+
|
192
|
+
if feature&.get_impact_campaign&.get_campaign_id
|
193
|
+
LoggerService.log(
|
194
|
+
LogLevelEnum::INFO,
|
195
|
+
"IMPACT_ANALYSIS",
|
196
|
+
{
|
197
|
+
userId: context.get_id,
|
198
|
+
featureKey: feature_key,
|
199
|
+
status: is_enabled ? 'enabled' : 'disabled'
|
200
|
+
}
|
201
|
+
)
|
202
|
+
|
203
|
+
variation_id = is_enabled ? 2 : 1 # 2 for Variation(flag enabled), 1 for Control(flag disabled)
|
204
|
+
|
205
|
+
create_and_send_impression_for_variation_shown(
|
206
|
+
settings,
|
207
|
+
feature&.get_impact_campaign&.get_campaign_id,
|
208
|
+
variation_id,
|
209
|
+
context
|
210
|
+
)
|
211
|
+
end
|
212
|
+
|
213
|
+
# Return final evaluated feature flag
|
214
|
+
return GetFlagResponse.new(is_enabled, experiment_variation_to_return&.get_variables || rollout_variation_to_return&.get_variables || [])
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def update_integrations_decision_object(campaign, variation, passed_rules_information, decision)
|
220
|
+
if campaign.get_type == CampaignTypeEnum::ROLLOUT
|
221
|
+
passed_rules_information.merge!(
|
222
|
+
rollout_id: campaign.get_id,
|
223
|
+
rollout_key: campaign.get_key,
|
224
|
+
rollout_variation_id: variation.get_id
|
225
|
+
)
|
226
|
+
else
|
227
|
+
passed_rules_information.merge!(
|
228
|
+
experiment_id: campaign.get_id,
|
229
|
+
experiment_key: campaign.get_key,
|
230
|
+
experiment_variation_id: variation.get_id
|
231
|
+
)
|
232
|
+
end
|
233
|
+
decision.merge!(passed_rules_information)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
@@ -0,0 +1,57 @@
|
|
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/user/context_model'
|
16
|
+
require_relative '../enums/event_enum'
|
17
|
+
require_relative '../utils/network_util'
|
18
|
+
require_relative '../models/settings/settings_model'
|
19
|
+
|
20
|
+
class SetAttributeApi
|
21
|
+
# Sets multiple attributes for a user in a single network call.
|
22
|
+
# @param settings [SettingsModel] Configuration settings.
|
23
|
+
# @param attributes [Hash] Key-value map of attributes.
|
24
|
+
# @param context [ContextModel] Context containing user information.
|
25
|
+
def set_attribute(settings, attributes, context)
|
26
|
+
create_impression_for_attributes(settings, attributes, context)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Creates an impression for multiple user attributes and sends it to the server.
|
32
|
+
# @param settings [SettingsModel] Configuration settings.
|
33
|
+
# @param attributes [Hash] Key-value map of attributes.
|
34
|
+
# @param context [ContextModel] Context containing user information.
|
35
|
+
def create_impression_for_attributes(settings, attributes, context)
|
36
|
+
# Retrieve base properties for the event
|
37
|
+
properties = NetworkUtil.get_events_base_properties(
|
38
|
+
settings,
|
39
|
+
EventEnum::VWO_SYNC_VISITOR_PROP,
|
40
|
+
URI.encode_www_form_component(context.user_agent),
|
41
|
+
context.ip_address
|
42
|
+
)
|
43
|
+
|
44
|
+
# Construct payload data for multiple attributes
|
45
|
+
payload = NetworkUtil.get_attribute_payload_data(
|
46
|
+
settings,
|
47
|
+
context.id,
|
48
|
+
EventEnum::VWO_SYNC_VISITOR_PROP,
|
49
|
+
attributes,
|
50
|
+
context.user_agent,
|
51
|
+
context.ip_address
|
52
|
+
)
|
53
|
+
|
54
|
+
# Send the constructed payload via POST request
|
55
|
+
NetworkUtil.send_post_api_request(properties, payload)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,77 @@
|
|
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/api_enum'
|
16
|
+
require_relative '../models/settings/settings_model'
|
17
|
+
require_relative '../models/user/context_model'
|
18
|
+
require_relative '../services/hooks_service'
|
19
|
+
require_relative '../utils/function_util'
|
20
|
+
require_relative '../utils/network_util'
|
21
|
+
require_relative '../services/logger_service'
|
22
|
+
class TrackApi
|
23
|
+
# Tracks an event with given properties and context.
|
24
|
+
# @param settings [SettingsModel] Configuration settings.
|
25
|
+
# @param event_name [String] Name of the event to track.
|
26
|
+
# @param context [ContextModel] Contextual information like user details.
|
27
|
+
# @param event_properties [Hash] Properties associated with the event.
|
28
|
+
# @param hooks_service [HooksService] Manager for handling hooks and callbacks.
|
29
|
+
# @return [Hash] A hash indicating success or failure of event tracking.
|
30
|
+
def track(settings, event_name, context, event_properties, hooks_service)
|
31
|
+
if does_event_belong_to_any_feature(event_name, settings)
|
32
|
+
# Create an impression for the track event
|
33
|
+
create_impression_for_track(settings, event_name, context, event_properties)
|
34
|
+
|
35
|
+
# Set and execute integration callback for the track event
|
36
|
+
hooks_service.set({ event_name: event_name, api: ApiEnum::TRACK })
|
37
|
+
hooks_service.execute(hooks_service.get)
|
38
|
+
|
39
|
+
return { event_name: true }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Log an error if the event does not exist
|
43
|
+
LoggerService.log(LogLevelEnum::ERROR, "EVENT_NOT_FOUND", { eventName: event_name })
|
44
|
+
|
45
|
+
{ event_name: false }
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Creates an impression for a track event and sends it via a POST API request.
|
51
|
+
# @param settings [SettingsModel] Configuration settings.
|
52
|
+
# @param event_name [String] Name of the event to track.
|
53
|
+
# @param context [ContextModel] User details.
|
54
|
+
# @param event_properties [Hash] Properties associated with the event.
|
55
|
+
def create_impression_for_track(settings, event_name, context, event_properties)
|
56
|
+
# Get base properties for the event
|
57
|
+
properties = NetworkUtil.get_events_base_properties(
|
58
|
+
settings,
|
59
|
+
event_name,
|
60
|
+
URI.encode_www_form_component(context.user_agent),
|
61
|
+
context.ip_address
|
62
|
+
)
|
63
|
+
|
64
|
+
# Prepare the payload for the track goal
|
65
|
+
payload = NetworkUtil.get_track_goal_payload_data(
|
66
|
+
settings,
|
67
|
+
context.id,
|
68
|
+
event_name,
|
69
|
+
event_properties,
|
70
|
+
context.user_agent,
|
71
|
+
context.ip_address
|
72
|
+
)
|
73
|
+
|
74
|
+
# Send the prepared payload via POST API request
|
75
|
+
NetworkUtil.send_post_api_request(properties, payload)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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
|
+
# Constants file for Ruby SDK
|
16
|
+
|
17
|
+
# Define the Constants module
|
18
|
+
module Constants
|
19
|
+
SDK_NAME = 'vwo-fme-ruby-sdk'.freeze
|
20
|
+
SDK_VERSION = '1.2.0'.freeze
|
21
|
+
|
22
|
+
MAX_TRAFFIC_PERCENT = 100
|
23
|
+
MAX_TRAFFIC_VALUE = 10_000
|
24
|
+
STATUS_RUNNING = 'RUNNING'.freeze
|
25
|
+
|
26
|
+
SEED_VALUE = 1
|
27
|
+
MAX_EVENTS_PER_REQUEST = 5_000
|
28
|
+
DEFAULT_REQUEST_TIME_INTERVAL = 600 # 10 minutes in seconds
|
29
|
+
DEFAULT_EVENTS_PER_REQUEST = 100
|
30
|
+
|
31
|
+
SEED_URL = 'https://vwo.com'.freeze # Define SEED_URL
|
32
|
+
HTTP_PROTOCOL = 'http'.freeze
|
33
|
+
HTTPS_PROTOCOL = 'https'.freeze
|
34
|
+
|
35
|
+
SETTINGS = 'settings'.freeze
|
36
|
+
SETTINGS_EXPIRY = 10_000_000
|
37
|
+
SETTINGS_TIMEOUT = 50_000
|
38
|
+
|
39
|
+
HOST_NAME = 'dev.visualwebsiteoptimizer.com'.freeze
|
40
|
+
SETTINGS_ENDPOINT = '/server-side/v2-settings'.freeze
|
41
|
+
WEBHOOK_SETTINGS_ENDPOINT = '/server-side/v2-pull'.freeze
|
42
|
+
LOCATION_ENDPOINT = '/getLocation'.freeze
|
43
|
+
|
44
|
+
VWO_FS_ENVIRONMENT = 'vwo_fs_environment'.freeze
|
45
|
+
|
46
|
+
RANDOM_ALGO = 1
|
47
|
+
|
48
|
+
API_VERSION = '1'.freeze
|
49
|
+
|
50
|
+
VWO_META_MEG_KEY = '_vwo_meta_meg_'.freeze
|
51
|
+
|
52
|
+
SHOULD_USE_THREADING = true
|
53
|
+
MAX_POOL_SIZE = 5
|
54
|
+
end
|
@@ -0,0 +1,86 @@
|
|
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/campaign/feature_model'
|
17
|
+
require_relative '../models/campaign/variation_model'
|
18
|
+
require_relative '../services/storage_service'
|
19
|
+
require_relative '../models/user/context_model'
|
20
|
+
require_relative '../services/logger_service'
|
21
|
+
require_relative '../enums/log_level_enum'
|
22
|
+
class StorageDecorator
|
23
|
+
# Retrieves a feature from storage based on the feature key and user.
|
24
|
+
# @param feature_key [String] The key of the feature to retrieve.
|
25
|
+
# @param context [ContextModel] The user object.
|
26
|
+
# @param storage_service [StorageService] The storage service instance.
|
27
|
+
# @return [Hash, StorageEnum] The retrieved feature or relevant status.
|
28
|
+
def get_feature_from_storage(feature_key, context, storage_service)
|
29
|
+
campaign_map = storage_service.get_data_from_storage(feature_key, context)
|
30
|
+
|
31
|
+
case campaign_map
|
32
|
+
when StorageEnum::STORAGE_UNDEFINED, StorageEnum::NO_DATA_FOUND,
|
33
|
+
StorageEnum::CAMPAIGN_PAUSED, StorageEnum::WHITELISTED_VARIATION
|
34
|
+
nil # Return nil when there's no valid data
|
35
|
+
when StorageEnum::INCORRECT_DATA, StorageEnum::VARIATION_NOT_FOUND
|
36
|
+
campaign_map # Return the relevant error constant
|
37
|
+
else
|
38
|
+
campaign_map # Return valid stored data
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sets data in storage based on the provided data object.
|
43
|
+
# @param data [Hash] The data to be stored, including feature key and user details.
|
44
|
+
# @param storage_service [StorageService] The storage service instance.
|
45
|
+
def set_data_in_storage(data, storage_service)
|
46
|
+
feature_key = data[:feature_key]
|
47
|
+
context = data[:context]
|
48
|
+
rollout_id = data[:rollout_id]
|
49
|
+
rollout_key = data[:rollout_key]
|
50
|
+
rollout_variation_id = data[:rollout_variation_id]
|
51
|
+
experiment_id = data[:experiment_id]
|
52
|
+
experiment_key = data[:experiment_key]
|
53
|
+
experiment_variation_id = data[:experiment_variation_id]
|
54
|
+
|
55
|
+
if feature_key.nil?
|
56
|
+
LoggerService.log(LogLevelEnum::ERROR, "STORING_DATA_ERROR", { key: 'featureKey' })
|
57
|
+
raise 'Feature key is missing'
|
58
|
+
end
|
59
|
+
|
60
|
+
if context.nil? || context.id.nil?
|
61
|
+
LoggerService.log(LogLevelEnum::ERROR, "STORING_DATA_ERROR", { key: 'Context or Context.id' })
|
62
|
+
raise 'Context ID is missing'
|
63
|
+
end
|
64
|
+
|
65
|
+
if rollout_key && !experiment_key && !rollout_variation_id
|
66
|
+
LoggerService.log(LogLevelEnum::ERROR, "STORING_DATA_ERROR", { key: 'Variation:(rolloutKey, experimentKey or rolloutVariationId)' })
|
67
|
+
raise 'Invalid rollout variation'
|
68
|
+
end
|
69
|
+
|
70
|
+
if experiment_key && !experiment_variation_id
|
71
|
+
LoggerService.log(LogLevelEnum::ERROR, "STORING_DATA_ERROR", { key: 'Variation:(experimentKey or rolloutVariationId)' })
|
72
|
+
raise 'Invalid experiment variation'
|
73
|
+
end
|
74
|
+
|
75
|
+
storage_service.set_data_in_storage({
|
76
|
+
feature_key: feature_key,
|
77
|
+
user_id: context.id,
|
78
|
+
rollout_id: rollout_id,
|
79
|
+
rollout_key: rollout_key,
|
80
|
+
rollout_variation_id: rollout_variation_id,
|
81
|
+
experiment_id: experiment_id,
|
82
|
+
experiment_key: experiment_key,
|
83
|
+
experiment_variation_id: experiment_variation_id
|
84
|
+
})
|
85
|
+
end
|
86
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2025 Wingify Software Pvt. Ltd.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -12,13 +12,8 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# Singleton logger instance
|
19
|
-
def self.logger
|
20
|
-
@logger ||= Logger.new(STDOUT) # You can log to a file like 'application.log'
|
21
|
-
@logger.level = Logger::DEBUG # Set the default logging level
|
22
|
-
@logger
|
15
|
+
module ApiEnum
|
16
|
+
GET_FLAG = 'getFlag'
|
17
|
+
TRACK = 'track'
|
23
18
|
end
|
24
|
-
|
19
|
+
|
@@ -0,0 +1,19 @@
|
|
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 CampaignTypeEnum
|
16
|
+
ROLLOUT = 'FLAG_ROLLOUT'
|
17
|
+
AB = 'FLAG_TESTING'
|
18
|
+
PERSONALIZE = 'FLAG_PERSONALIZE'
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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 DecisionTypes
|
16
|
+
CAMPAIGN_DECISION = 'CAMPAIGN_DECISION'
|
17
|
+
end
|
18
|
+
|