wingify-fme-ruby-sdk 1.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/resources/debug_messages.json +21 -0
- data/lib/resources/error_messages.json +63 -0
- data/lib/resources/info_messages.json +43 -0
- data/lib/resources/warn_messages.json +1 -0
- data/lib/vwo.rb +40 -0
- data/lib/wingify/api/get_flag.rb +244 -0
- data/lib/wingify/api/set_attribute.rb +57 -0
- data/lib/wingify/api/track_event.rb +80 -0
- data/lib/wingify/constants/constants.rb +106 -0
- data/lib/wingify/decorators/storage_decorator.rb +82 -0
- data/lib/wingify/enums/api_enum.rb +23 -0
- data/lib/wingify/enums/campaign_type_enum.rb +19 -0
- data/lib/wingify/enums/debug_category_enum.rb +20 -0
- data/lib/wingify/enums/decision_types_enum.rb +18 -0
- data/lib/wingify/enums/event_enum.rb +22 -0
- data/lib/wingify/enums/headers_enum.rb +20 -0
- data/lib/wingify/enums/hooks_enum.rb +17 -0
- data/lib/wingify/enums/http_method_enum.rb +18 -0
- data/lib/wingify/enums/log_level_enum.rb +21 -0
- data/lib/wingify/enums/log_level_to_number.rb +27 -0
- data/lib/wingify/enums/status_enum.rb +19 -0
- data/lib/wingify/enums/storage_enum.rb +22 -0
- data/lib/wingify/enums/url_enum.rb +22 -0
- data/lib/wingify/models/campaign/campaign_model.rb +192 -0
- data/lib/wingify/models/campaign/feature_model.rb +111 -0
- data/lib/wingify/models/campaign/impact_campaign_model.rb +38 -0
- data/lib/wingify/models/campaign/metric_model.rb +44 -0
- data/lib/wingify/models/campaign/rule_model.rb +56 -0
- data/lib/wingify/models/campaign/variable_model.rb +51 -0
- data/lib/wingify/models/campaign/variation_model.rb +137 -0
- data/lib/wingify/models/gateway_service_model.rb +39 -0
- data/lib/wingify/models/schemas/settings_schema_validation.rb +104 -0
- data/lib/wingify/models/settings/settings_model.rb +101 -0
- data/lib/wingify/models/storage/storage_data_model.rb +44 -0
- data/lib/wingify/models/user/context_model.rb +154 -0
- data/lib/wingify/models/user/context_vwo_model.rb +38 -0
- data/lib/wingify/models/user/get_flag_response.rb +50 -0
- data/lib/wingify/models/vwo_options_model.rb +129 -0
- data/lib/wingify/packages/decision_maker/decision_maker.rb +60 -0
- data/lib/wingify/packages/logger/core/log_manager.rb +92 -0
- data/lib/wingify/packages/logger/core/transport_manager.rb +76 -0
- data/lib/wingify/packages/logger/log_message_builder.rb +70 -0
- data/lib/wingify/packages/logger/logger.rb +38 -0
- data/lib/wingify/packages/logger/transports/console_transport.rb +49 -0
- data/lib/wingify/packages/network_layer/client/network_client.rb +276 -0
- data/lib/wingify/packages/network_layer/handlers/request_handler.rb +37 -0
- data/lib/wingify/packages/network_layer/manager/network_manager.rb +145 -0
- data/lib/wingify/packages/network_layer/models/global_request_model.rb +105 -0
- data/lib/wingify/packages/network_layer/models/request_model.rb +179 -0
- data/lib/wingify/packages/network_layer/models/response_model.rb +62 -0
- data/lib/wingify/packages/segmentation_evaluator/core/segmentation_manager.rb +77 -0
- data/lib/wingify/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +29 -0
- data/lib/wingify/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +26 -0
- data/lib/wingify/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +33 -0
- data/lib/wingify/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +218 -0
- data/lib/wingify/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +415 -0
- data/lib/wingify/packages/segmentation_evaluator/utils/segment_util.rb +44 -0
- data/lib/wingify/packages/storage/connector.rb +26 -0
- data/lib/wingify/packages/storage/storage.rb +47 -0
- data/lib/wingify/services/batch_event_queue.rb +179 -0
- data/lib/wingify/services/campaign_decision_service.rb +161 -0
- data/lib/wingify/services/hooks_service.rb +51 -0
- data/lib/wingify/services/logger_service.rb +114 -0
- data/lib/wingify/services/settings_service.rb +178 -0
- data/lib/wingify/services/storage_service.rb +66 -0
- data/lib/wingify/utils/batch_event_dispatcher_util.rb +178 -0
- data/lib/wingify/utils/brand_context.rb +33 -0
- data/lib/wingify/utils/brand_util.rb +53 -0
- data/lib/wingify/utils/campaign_util.rb +284 -0
- data/lib/wingify/utils/data_type_util.rb +105 -0
- data/lib/wingify/utils/debugger_service_util.rb +40 -0
- data/lib/wingify/utils/decision_util.rb +259 -0
- data/lib/wingify/utils/event_util.rb +55 -0
- data/lib/wingify/utils/function_util.rb +141 -0
- data/lib/wingify/utils/gateway_service_util.rb +101 -0
- data/lib/wingify/utils/impression_util.rb +66 -0
- data/lib/wingify/utils/log_message_util.rb +42 -0
- data/lib/wingify/utils/meg_util.rb +357 -0
- data/lib/wingify/utils/network_util.rb +503 -0
- data/lib/wingify/utils/rule_evaluation_util.rb +57 -0
- data/lib/wingify/utils/settings_util.rb +38 -0
- data/lib/wingify/utils/usage_stats_util.rb +119 -0
- data/lib/wingify/utils/uuid_util.rb +96 -0
- data/lib/wingify/wingify_builder.rb +261 -0
- data/lib/wingify/wingify_client.rb +227 -0
- data/lib/wingify.rb +117 -0
- metadata +327 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Copyright 2024-2026 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
|
+
require_relative '../enums/api_enum'
|
|
25
|
+
require_relative '../utils/debugger_service_util'
|
|
26
|
+
require_relative '../utils/brand_context'
|
|
27
|
+
require_relative '../utils/brand_util'
|
|
28
|
+
|
|
29
|
+
class SettingsService
|
|
30
|
+
attr_accessor :sdk_key, :account_id, :expiry, :network_timeout, :hostname, :events_hostname, :sdk_name, :port, :protocol, :is_gateway_service_provided, :is_settings_valid, :settings_fetch_time, :proxy_url, :is_proxy_url_provided, :collection_prefix
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
attr_accessor :instance
|
|
34
|
+
|
|
35
|
+
def get_instance
|
|
36
|
+
@instance ||= SettingsService.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def normalize_settings(settings)
|
|
40
|
+
normalized_settings = settings.dup
|
|
41
|
+
normalized_settings['features'] = [] if normalized_settings['features'].is_a?(Hash) && normalized_settings['features'].empty?
|
|
42
|
+
normalized_settings['campaigns'] = [] if normalized_settings['campaigns'].is_a?(Hash) && normalized_settings['campaigns'].empty?
|
|
43
|
+
normalized_settings
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize(options)
|
|
48
|
+
@sdk_key = options[:sdk_key]
|
|
49
|
+
@account_id = options[:account_id]
|
|
50
|
+
@expiry = options.dig(:settings, :expiry) || Constants::SETTINGS_EXPIRY
|
|
51
|
+
@network_timeout = options.dig(:settings, :timeout) || Constants::SETTINGS_TIMEOUT
|
|
52
|
+
@is_settings_valid = false
|
|
53
|
+
|
|
54
|
+
if options[:gateway_service] && options[:gateway_service][:url]
|
|
55
|
+
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]}")
|
|
56
|
+
@hostname = parsed_url.hostname
|
|
57
|
+
@events_hostname = parsed_url.hostname
|
|
58
|
+
@sdk_name = BrandUtil.get_sdk_name(BrandContext.is_via_vwo?)
|
|
59
|
+
@protocol = parsed_url.scheme
|
|
60
|
+
@port = parsed_url.port || options.dig(:gateway_service, :port)
|
|
61
|
+
@is_gateway_service_provided = true
|
|
62
|
+
if options[:proxy_url] && !options[:proxy_url].nil? && !options[:proxy_url].empty?
|
|
63
|
+
LoggerService.log(LogLevelEnum::INFO, "PROXY_AND_GATEWAY_SERVICE_PROVIDED")
|
|
64
|
+
end
|
|
65
|
+
elsif options[:proxy_url] && !options[:proxy_url].nil? && !options[:proxy_url].empty?
|
|
66
|
+
parsed_url = URI.parse(options[:proxy_url])
|
|
67
|
+
@hostname = parsed_url.hostname
|
|
68
|
+
@events_hostname = parsed_url.hostname
|
|
69
|
+
@sdk_name = BrandUtil.get_sdk_name(BrandContext.is_via_vwo?)
|
|
70
|
+
@protocol = parsed_url.scheme
|
|
71
|
+
@port = parsed_url.port
|
|
72
|
+
@is_proxy_url_provided = true
|
|
73
|
+
else
|
|
74
|
+
is_via_vwo = BrandContext.is_via_vwo?
|
|
75
|
+
@hostname = BrandUtil.get_settings_hostname(is_via_vwo)
|
|
76
|
+
@events_hostname = BrandUtil.get_events_hostname(is_via_vwo)
|
|
77
|
+
@sdk_name = BrandUtil.get_sdk_name(is_via_vwo)
|
|
78
|
+
@protocol = Constants::HTTPS_PROTOCOL
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
LoggerService.log(LogLevelEnum::DEBUG, "SERVICE_INITIALIZED", { service: 'Settings Manager' })
|
|
82
|
+
SettingsService.instance = self
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def get_updated_endpoint_with_collection_prefix(endpoint, is_gateway_provided = false)
|
|
86
|
+
if is_gateway_provided
|
|
87
|
+
return endpoint
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
if @collection_prefix && !@collection_prefix.empty?
|
|
91
|
+
return "/#{@collection_prefix}#{endpoint}"
|
|
92
|
+
end
|
|
93
|
+
return endpoint
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Fetch settings and cache them in storage.
|
|
97
|
+
# @return [SettingsModel] The fetched settings
|
|
98
|
+
def fetch_settings_and_cache_in_storage
|
|
99
|
+
begin
|
|
100
|
+
response = fetch_settings
|
|
101
|
+
response
|
|
102
|
+
rescue => e
|
|
103
|
+
LoggerService.log(LogLevelEnum::ERROR, "ERROR_FETCHING_SETTINGS", { err: e.message, an: ApiEnum::INIT}, false)
|
|
104
|
+
{}
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Fetch settings from the server.
|
|
109
|
+
# @param is_via_webhook [Boolean] Whether to fetch settings via webhook
|
|
110
|
+
# @return [SettingsModel] The fetched settings
|
|
111
|
+
def fetch_settings(is_via_webhook = false)
|
|
112
|
+
if @sdk_key.nil? || @account_id.nil?
|
|
113
|
+
LoggerService.log(LogLevelEnum::ERROR, "INVALID_SDK_KEY_OR_ACCOUNT_ID", { an: ApiEnum::INIT})
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
network_instance = NetworkManager.instance
|
|
117
|
+
options = NetworkUtil.get_settings_path(@sdk_key, @account_id)
|
|
118
|
+
|
|
119
|
+
options['api-version'] = Constants::API_VERSION
|
|
120
|
+
options[:source] = 'prod'
|
|
121
|
+
options[:sn] = @sdk_name
|
|
122
|
+
options[:sv] = Constants::SDK_VERSION
|
|
123
|
+
|
|
124
|
+
# When using gateway service, always fetch from SETTINGS_ENDPOINT since the gateway maintains the latest settings
|
|
125
|
+
if @is_gateway_service_provided
|
|
126
|
+
path = Constants::SETTINGS_ENDPOINT
|
|
127
|
+
else
|
|
128
|
+
path = is_via_webhook ? Constants::WEBHOOK_SETTINGS_ENDPOINT : Constants::SETTINGS_ENDPOINT
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
request = RequestModel.new(@hostname, "GET", path, options, nil, nil, @protocol, @port)
|
|
132
|
+
request.set_timeout(@network_timeout)
|
|
133
|
+
|
|
134
|
+
# store the current time in milliseconds
|
|
135
|
+
settings_fetch_start_time = (Time.now.to_f * 1000).to_i
|
|
136
|
+
|
|
137
|
+
begin
|
|
138
|
+
response = network_instance.get(request)
|
|
139
|
+
# calculate the time taken to fetch the settings
|
|
140
|
+
settings_fetch_end_time = (Time.now.to_f * 1000).to_i
|
|
141
|
+
time_taken = settings_fetch_end_time - settings_fetch_start_time
|
|
142
|
+
@settings_fetch_time = time_taken.to_s
|
|
143
|
+
|
|
144
|
+
if response.get_total_attempts > 0
|
|
145
|
+
api_enum = is_via_webhook ? ApiEnum::UPDATE_SETTINGS : ApiEnum::INIT
|
|
146
|
+
debug_event_props = NetworkUtil.create_network_and_retry_debug_event(response, nil, api_enum, path)
|
|
147
|
+
# send debug event
|
|
148
|
+
DebuggerServiceUtil.send_debugger_event(debug_event_props)
|
|
149
|
+
end
|
|
150
|
+
settings = response.get_data
|
|
151
|
+
if settings.nil? || settings.empty?
|
|
152
|
+
settings = {}
|
|
153
|
+
end
|
|
154
|
+
# Deep duplicate the settings to avoid modifying the original object
|
|
155
|
+
normalized_settings = SettingsService.normalize_settings(settings)
|
|
156
|
+
@collection_prefix = normalized_settings['collectionPrefix'] if normalized_settings['collectionPrefix'] && !normalized_settings['collectionPrefix'].empty?
|
|
157
|
+
normalized_settings
|
|
158
|
+
rescue => e
|
|
159
|
+
LoggerService.log(LogLevelEnum::ERROR, "ERROR_FETCHING_SETTINGS", { err: e.message, an: ApiEnum::INIT})
|
|
160
|
+
{}
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Get settings (either from storage or by forcing fetch).
|
|
165
|
+
# @return [SettingsModel] The fetched settings
|
|
166
|
+
def get_settings
|
|
167
|
+
settings = fetch_settings_and_cache_in_storage
|
|
168
|
+
is_valid = SettingsSchema.new.is_settings_valid(settings)
|
|
169
|
+
if is_valid
|
|
170
|
+
@is_settings_valid = true
|
|
171
|
+
LoggerService.log(LogLevelEnum::INFO, "SETTINGS_FETCH_SUCCESS")
|
|
172
|
+
settings
|
|
173
|
+
else
|
|
174
|
+
LoggerService.log(LogLevelEnum::ERROR, "INVALID_SETTINGS_SCHEMA", { accountId: @account_id, sdkKey: @sdk_key, settings: settings, an: ApiEnum::INIT}, false)
|
|
175
|
+
{}
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Copyright 2024-2026 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
|
+
require_relative '../enums/api_enum'
|
|
22
|
+
|
|
23
|
+
class StorageService
|
|
24
|
+
attr_accessor :storage_data
|
|
25
|
+
|
|
26
|
+
def initialize
|
|
27
|
+
@storage_data = {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Retrieves data from storage based on the feature key and user ID.
|
|
31
|
+
# @param feature_key [String] The key to identify the feature data.
|
|
32
|
+
# @param context [ContextModel] The user context containing an ID.
|
|
33
|
+
# @return [Hash] The data retrieved or a storage status enum.
|
|
34
|
+
def get_data_from_storage(feature_key, context)
|
|
35
|
+
storage_instance = Storage.instance.get_connector
|
|
36
|
+
|
|
37
|
+
# Check if the storage instance is available
|
|
38
|
+
return StorageEnum::STORAGE_UNDEFINED if DataTypeUtil.is_null(storage_instance) || DataTypeUtil.is_undefined(storage_instance)
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
data = storage_instance.get(feature_key, context.get_id)
|
|
42
|
+
return data.nil? ? StorageEnum::NO_DATA_FOUND : data
|
|
43
|
+
rescue StandardError => e
|
|
44
|
+
LoggerService.log(LogLevelEnum::ERROR, "ERROR_READING_DATA_FROM_STORAGE", { err: e.message, an: ApiEnum::GET_FLAG, sId: context.get_session_id, uuid: context.get_uuid})
|
|
45
|
+
return StorageEnum::NO_DATA_FOUND
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Stores data in the storage.
|
|
50
|
+
# @param data [Hash] The data to be stored.
|
|
51
|
+
# @return [Boolean] True if data is successfully stored, otherwise false.
|
|
52
|
+
def set_data_in_storage(data, context)
|
|
53
|
+
storage_instance = Storage.instance.get_connector
|
|
54
|
+
|
|
55
|
+
# Check if the storage instance is available
|
|
56
|
+
return false if storage_instance.nil?
|
|
57
|
+
|
|
58
|
+
begin
|
|
59
|
+
storage_instance.set(data)
|
|
60
|
+
return true
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
LoggerService.log(LogLevelEnum::ERROR, "ERROR_STORING_DATA_IN_STORAGE", { err: e.message, an: ApiEnum::GET_FLAG, sId: context.get_session_id, uuid: context.get_uuid})
|
|
63
|
+
return false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Copyright 2024-2026 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 'set'
|
|
16
|
+
require_relative './network_util'
|
|
17
|
+
require_relative '../enums/http_method_enum'
|
|
18
|
+
require_relative '../enums/url_enum'
|
|
19
|
+
require_relative '../enums/log_level_enum'
|
|
20
|
+
require_relative '../enums/event_enum'
|
|
21
|
+
require_relative '../services/logger_service'
|
|
22
|
+
require_relative '../packages/network_layer/manager/network_manager'
|
|
23
|
+
require_relative '../packages/network_layer/models/request_model'
|
|
24
|
+
require_relative '../packages/network_layer/models/response_model'
|
|
25
|
+
require_relative '../utils/function_util'
|
|
26
|
+
|
|
27
|
+
class BatchEventDispatcherUtil
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
|
|
31
|
+
# Dispatches a batch of events to the VWO server
|
|
32
|
+
# @param properties [Hash] The event properties to send
|
|
33
|
+
# @param callback [Proc] Optional callback function to execute after the request (defaults to empty proc)
|
|
34
|
+
# @param query_params [Hash] Query parameters to include in the request
|
|
35
|
+
def dispatch(properties, callback = -> {}, query_params)
|
|
36
|
+
# Send the prepared payload via POST API request
|
|
37
|
+
send_batch_post_api_request(query_params, properties, callback)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Sends a POST API request with given properties and payload
|
|
41
|
+
def send_batch_post_api_request(properties, payload, callback)
|
|
42
|
+
network_instance = NetworkManager.instance
|
|
43
|
+
headers = {}
|
|
44
|
+
headers['Authorization'] = "#{SettingsService.instance.sdk_key}"
|
|
45
|
+
|
|
46
|
+
request = RequestModel.new(
|
|
47
|
+
SettingsService.instance.events_hostname,
|
|
48
|
+
HttpMethodEnum::POST,
|
|
49
|
+
SettingsService.instance.get_updated_endpoint_with_collection_prefix(UrlEnum::BATCH_EVENTS, SettingsService.instance.is_gateway_service_provided),
|
|
50
|
+
properties,
|
|
51
|
+
payload,
|
|
52
|
+
headers,
|
|
53
|
+
SettingsService.instance.protocol,
|
|
54
|
+
SettingsService.instance.port
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
event_counts = extract_event_counts(payload)
|
|
58
|
+
extra_data = "#{Constants::BATCH_EVENTS} having"
|
|
59
|
+
if event_counts[:variation_shown_count] > 0
|
|
60
|
+
extra_data += "getFlag events: #{event_counts[:variation_shown_count]}, "
|
|
61
|
+
end
|
|
62
|
+
if event_counts[:custom_event_count] > 0
|
|
63
|
+
extra_data += "conversion events: #{event_counts[:custom_event_count]}, "
|
|
64
|
+
end
|
|
65
|
+
if event_counts[:set_attribute_count] > 0
|
|
66
|
+
extra_data += "setAttribute events: #{event_counts[:set_attribute_count]}, "
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
begin
|
|
70
|
+
response = network_instance.post(request)
|
|
71
|
+
# Only send debug event if response is valid and has retry attempts
|
|
72
|
+
if response.is_a?(ResponseModel) && response.get_total_attempts && response.get_total_attempts > 0
|
|
73
|
+
debug_event_props = NetworkUtil.create_network_and_retry_debug_event(response, nil, Constants::BATCH_EVENTS, extra_data)
|
|
74
|
+
# send debug event
|
|
75
|
+
DebuggerServiceUtil.send_debugger_event(debug_event_props) if debug_event_props
|
|
76
|
+
end
|
|
77
|
+
handle_batch_response(UrlEnum::BATCH_EVENTS, payload, properties, response, response.get_data, callback)
|
|
78
|
+
rescue StandardError => err
|
|
79
|
+
# TODO: remove this log after testing
|
|
80
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
|
81
|
+
method: extra_data,
|
|
82
|
+
err: get_formatted_error_message(err)
|
|
83
|
+
})
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Handles the response from a batch event API call
|
|
88
|
+
# @param end_point [String] The API endpoint that was called
|
|
89
|
+
# @param payload [Hash] The payload that was sent in the request
|
|
90
|
+
# @param query_params [Hash] The query parameters used in the request
|
|
91
|
+
# @param res [ResponseModel] The response object from the API call
|
|
92
|
+
# @param raw_data [String] The raw response data from the API
|
|
93
|
+
# @param callback [Proc] Optional callback to be executed after handling the response
|
|
94
|
+
def handle_batch_response(end_point, payload, query_params, res, raw_data, callback)
|
|
95
|
+
# TODO: update this method with debug event logs
|
|
96
|
+
events_per_request = payload[:ev].length
|
|
97
|
+
account_id = query_params[:a]
|
|
98
|
+
|
|
99
|
+
error = res.get_error
|
|
100
|
+
if error
|
|
101
|
+
LoggerService.log(LogLevelEnum::INFO, "IMPRESSION_BATCH_FAILED")
|
|
102
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
|
103
|
+
method: "#{HttpMethodEnum::POST} #{UrlEnum::BATCH_EVENTS}",
|
|
104
|
+
err: error
|
|
105
|
+
}, false)
|
|
106
|
+
callback.call(error, payload.to_json) if callback.respond_to?(:call)
|
|
107
|
+
return {status: "error", events: payload}
|
|
108
|
+
else
|
|
109
|
+
case res.get_status_code
|
|
110
|
+
when 200
|
|
111
|
+
LoggerService.log(LogLevelEnum::INFO, "IMPRESSION_BATCH_SUCCESS", {
|
|
112
|
+
accountId: account_id,
|
|
113
|
+
endPoint: end_point,
|
|
114
|
+
})
|
|
115
|
+
callback.call(nil, payload.to_json) if callback.respond_to?(:call)
|
|
116
|
+
return {status: "success", events: payload}
|
|
117
|
+
when 413
|
|
118
|
+
LoggerService.log(LogLevelEnum::DEBUG, "CONFIG_BATCH_EVENT_LIMIT_EXCEEDED", {
|
|
119
|
+
accountId: account_id,
|
|
120
|
+
endPoint: end_point,
|
|
121
|
+
eventsPerRequest: events_per_request
|
|
122
|
+
})
|
|
123
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
|
124
|
+
method: "#{HttpMethodEnum::POST} #{UrlEnum::BATCH_EVENTS}",
|
|
125
|
+
err: error
|
|
126
|
+
}, false)
|
|
127
|
+
callback.call(error, payload.to_json) if callback.respond_to?(:call)
|
|
128
|
+
return {status: "error", events: payload}
|
|
129
|
+
else
|
|
130
|
+
LoggerService.log(LogLevelEnum::INFO, "IMPRESSION_BATCH_FAILED")
|
|
131
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
|
132
|
+
method: "#{HttpMethodEnum::POST} #{UrlEnum::BATCH_EVENTS}",
|
|
133
|
+
err: error
|
|
134
|
+
}, false)
|
|
135
|
+
callback.call(error, payload.to_json) if callback.respond_to?(:call)
|
|
136
|
+
return {status: "error", events: payload}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Extracts event counts from a batch payload
|
|
142
|
+
# @param payload [Hash] The payload containing events
|
|
143
|
+
# @return [Hash] Hash with variationShownCount, setAttributeCount, and customEventCount
|
|
144
|
+
def extract_event_counts(payload)
|
|
145
|
+
counts = {
|
|
146
|
+
variation_shown_count: 0,
|
|
147
|
+
set_attribute_count: 0,
|
|
148
|
+
custom_event_count: 0
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Get all standard event names from EventEnum
|
|
152
|
+
standard_event_names = EventEnum.constants.map { |const| EventEnum.const_get(const) }.to_set
|
|
153
|
+
events = payload&.dig(:ev) || []
|
|
154
|
+
|
|
155
|
+
events.each do |entry|
|
|
156
|
+
name = entry&.dig(:d, :event, :name)
|
|
157
|
+
|
|
158
|
+
next unless name
|
|
159
|
+
|
|
160
|
+
if name == EventEnum::VARIATION_SHOWN
|
|
161
|
+
counts[:variation_shown_count] += 1
|
|
162
|
+
next
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if name == EventEnum::SYNC_VISITOR_PROP
|
|
166
|
+
counts[:set_attribute_count] += 1
|
|
167
|
+
next
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
unless standard_event_names.include?(name)
|
|
171
|
+
counts[:custom_event_count] += 1
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
counts
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright 2024-2026 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
|
+
# BrandContext holds a single runtime flag that is set ONCE at init() time,
|
|
16
|
+
# before any service (Logger, SettingsService, etc.) is initialized.
|
|
17
|
+
# All brand-specific decisions (hostnames, SDK name, log prefix) read this flag.
|
|
18
|
+
module BrandContext
|
|
19
|
+
@is_via_vwo = false
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# Set the brand flag. Must be called as the very first step inside init().
|
|
23
|
+
# @param val [Boolean] true = VWO brand, false = Wingify brand
|
|
24
|
+
def set_is_via_vwo(val)
|
|
25
|
+
@is_via_vwo = val == true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Boolean] true if the active brand is VWO
|
|
29
|
+
def is_via_vwo?
|
|
30
|
+
@is_via_vwo
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Copyright 2024-2026 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
|
+
|
|
17
|
+
# BrandUtil provides pure, side-effect-free selectors for brand-specific constants.
|
|
18
|
+
# Every method takes an explicit is_via_vwo boolean so it can be used
|
|
19
|
+
# both at init time (reading BrandContext) and in tests (passed directly).
|
|
20
|
+
module BrandUtil
|
|
21
|
+
# Returns the gem/SDK name sent in network requests.
|
|
22
|
+
# Values: 'vwo-fme-ruby-sdk' or 'wingify-fme-ruby-sdk'
|
|
23
|
+
def self.get_sdk_name(is_via_vwo)
|
|
24
|
+
is_via_vwo ? Constants::VWO_SDK_NAME : Constants::WINGIFY_SDK_NAME
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the hostname used to FETCH SETTINGS.
|
|
28
|
+
# VWO: dev.visualwebsiteoptimizer.com
|
|
29
|
+
# Wingify: edge.wingify.net
|
|
30
|
+
def self.get_settings_hostname(is_via_vwo)
|
|
31
|
+
is_via_vwo ? Constants::VWO_HOST_NAME : Constants::WINGIFY_SETTINGS_HOST_NAME
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns the hostname used for ALL COLLECT / EVENT calls
|
|
35
|
+
# (track user, track goal, set attribute, batch events, usage stats, debugger events, SDK init event).
|
|
36
|
+
# VWO: dev.visualwebsiteoptimizer.com
|
|
37
|
+
# Wingify: collect.wingify.net
|
|
38
|
+
def self.get_events_hostname(is_via_vwo)
|
|
39
|
+
is_via_vwo ? Constants::VWO_HOST_NAME : Constants::WINGIFY_COLLECTION_HOST_NAME
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns the log prefix shown in every log line.
|
|
43
|
+
# Values: 'VWO-SDK' or 'Wingify-SDK'
|
|
44
|
+
def self.get_log_prefix(is_via_vwo)
|
|
45
|
+
is_via_vwo ? Constants::VWO_LOG_PREFIX : Constants::WINGIFY_LOG_PREFIX
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns the human-readable brand display name used in messages.
|
|
49
|
+
# Values: 'VWO' or 'Wingify'
|
|
50
|
+
def self.get_brand_name(is_via_vwo)
|
|
51
|
+
is_via_vwo ? Constants::VWO_BRAND_DISPLAY_NAME : Constants::WINGIFY_BRAND_DISPLAY_NAME
|
|
52
|
+
end
|
|
53
|
+
end
|