vwo-fme-ruby-sdk 1.2.0 → 1.3.1
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 +8 -1
- data/lib/resources/error_messages.json +5 -1
- data/lib/resources/info_messages.json +9 -1
- data/lib/vwo/api/get_flag.rb +30 -27
- data/lib/vwo/api/set_attribute.rb +10 -3
- data/lib/vwo/api/track_event.rb +11 -3
- data/lib/vwo/constants/constants.rb +6 -2
- data/lib/vwo/decorators/storage_decorator.rb +1 -1
- data/lib/vwo/enums/api_enum.rb +1 -1
- data/lib/vwo/enums/campaign_type_enum.rb +1 -1
- data/lib/vwo/enums/decision_types_enum.rb +1 -1
- data/lib/vwo/enums/event_enum.rb +1 -1
- data/lib/vwo/enums/headers_enum.rb +1 -1
- data/lib/vwo/enums/hooks_enum.rb +1 -1
- data/lib/vwo/enums/http_method_enum.rb +1 -1
- data/lib/vwo/enums/log_level_enum.rb +1 -1
- data/lib/vwo/enums/log_level_to_number.rb +27 -0
- data/lib/vwo/enums/status_enum.rb +1 -1
- data/lib/vwo/enums/storage_enum.rb +1 -1
- data/lib/vwo/enums/url_enum.rb +2 -1
- data/lib/vwo/models/campaign/campaign_model.rb +1 -1
- data/lib/vwo/models/campaign/feature_model.rb +1 -1
- data/lib/vwo/models/campaign/impact_campaign_model.rb +1 -1
- data/lib/vwo/models/campaign/metric_model.rb +1 -1
- data/lib/vwo/models/campaign/rule_model.rb +1 -1
- data/lib/vwo/models/campaign/variable_model.rb +1 -1
- data/lib/vwo/models/campaign/variation_model.rb +1 -1
- data/lib/vwo/models/gateway_service_model.rb +1 -1
- data/lib/vwo/models/schemas/settings_schema_validation.rb +1 -1
- data/lib/vwo/models/settings/settings_model.rb +8 -2
- data/lib/vwo/models/storage/storage_data_model.rb +1 -1
- data/lib/vwo/models/user/context_model.rb +1 -1
- data/lib/vwo/models/user/context_vwo_model.rb +1 -1
- data/lib/vwo/models/user/get_flag_response.rb +1 -1
- data/lib/vwo/models/vwo_options_model.rb +13 -2
- data/lib/vwo/packages/decision_maker/decision_maker.rb +1 -1
- data/lib/vwo/packages/logger/core/log_manager.rb +1 -1
- data/lib/vwo/packages/logger/core/transport_manager.rb +4 -15
- data/lib/vwo/packages/logger/log_message_builder.rb +1 -1
- data/lib/vwo/packages/logger/logger.rb +1 -1
- data/lib/vwo/packages/logger/transports/console_transport.rb +1 -1
- data/lib/vwo/packages/network_layer/client/network_client.rb +39 -25
- data/lib/vwo/packages/network_layer/handlers/request_handler.rb +1 -1
- data/lib/vwo/packages/network_layer/manager/network_manager.rb +6 -4
- data/lib/vwo/packages/network_layer/models/global_request_model.rb +1 -1
- data/lib/vwo/packages/network_layer/models/request_model.rb +1 -1
- data/lib/vwo/packages/network_layer/models/response_model.rb +9 -1
- data/lib/vwo/packages/segmentation_evaluator/core/segmentation_manager.rb +1 -1
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +1 -1
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +1 -1
- data/lib/vwo/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +1 -1
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +1 -1
- data/lib/vwo/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +1 -1
- data/lib/vwo/packages/segmentation_evaluator/utils/segment_util.rb +1 -1
- data/lib/vwo/packages/storage/connector.rb +1 -1
- data/lib/vwo/packages/storage/storage.rb +3 -1
- data/lib/vwo/services/batch_event_queue.rb +179 -0
- data/lib/vwo/services/campaign_decision_service.rb +1 -1
- data/lib/vwo/services/hooks_service.rb +1 -1
- data/lib/vwo/services/logger_service.rb +1 -1
- data/lib/vwo/services/settings_service.rb +4 -2
- data/lib/vwo/services/storage_service.rb +1 -1
- data/lib/vwo/utils/batch_event_dispatcher.rb +117 -0
- data/lib/vwo/utils/campaign_util.rb +1 -1
- data/lib/vwo/utils/data_type_util.rb +1 -1
- data/lib/vwo/utils/decision_util.rb +1 -1
- data/lib/vwo/utils/function_util.rb +5 -1
- data/lib/vwo/utils/gateway_service_util.rb +1 -1
- data/lib/vwo/utils/impression_util.rb +10 -3
- data/lib/vwo/utils/log_message_util.rb +1 -1
- data/lib/vwo/utils/meg_util.rb +1 -1
- data/lib/vwo/utils/network_util.rb +31 -8
- data/lib/vwo/utils/rule_evaluation_util.rb +1 -1
- data/lib/vwo/utils/settings_util.rb +1 -1
- data/lib/vwo/utils/url_util.rb +1 -1
- data/lib/vwo/utils/usage_stats_util.rb +117 -0
- data/lib/vwo/utils/uuid_util.rb +1 -1
- data/lib/vwo/vwo_builder.rb +107 -24
- data/lib/vwo/vwo_client.rb +23 -4
- data/lib/vwo.rb +3 -1
- metadata +6 -2
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2025 Wingify Software Pvt. Ltd.
|
1
|
+
# Copyright 2024-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.
|
@@ -27,13 +27,26 @@ class NetworkClient
|
|
27
27
|
def initialize(options = {})
|
28
28
|
# options for threading
|
29
29
|
@should_use_threading = options.key?(:enabled) ? options[:enabled] : Constants::SHOULD_USE_THREADING
|
30
|
-
@thread_pool = Concurrent::
|
30
|
+
@thread_pool = Concurrent::ThreadPoolExecutor.new(
|
31
|
+
# Minimum number of threads to keep alive in the pool
|
32
|
+
min_threads: 1,
|
33
|
+
# Maximum number of threads allowed in the pool, configurable via options or defaults to MAX_POOL_SIZE constant
|
34
|
+
max_threads: options.key?(:max_pool_size) ? options[:max_pool_size] : Constants::MAX_POOL_SIZE,
|
35
|
+
# Maximum number of tasks that can be queued when all threads are busy
|
36
|
+
max_queue: options.key?(:max_queue_size) ? options[:max_queue_size] : Constants::MAX_QUEUE_SIZE,
|
37
|
+
# When queue is full, execute task in the caller's thread rather than rejecting it
|
38
|
+
fallback_policy: :caller_runs
|
39
|
+
)
|
31
40
|
end
|
32
41
|
|
33
42
|
def get_thread_pool
|
34
43
|
@thread_pool
|
35
44
|
end
|
36
45
|
|
46
|
+
def get_should_use_threading
|
47
|
+
@should_use_threading
|
48
|
+
end
|
49
|
+
|
37
50
|
def get(request_model)
|
38
51
|
# Build the URL and headers
|
39
52
|
url = request_model.get_url + request_model.get_path
|
@@ -67,22 +80,24 @@ class NetworkClient
|
|
67
80
|
end
|
68
81
|
|
69
82
|
def post(request_model)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
request.body = body
|
78
|
-
|
79
|
-
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }
|
80
|
-
|
81
|
-
response_model = ResponseModel.new
|
82
|
-
response_model.set_status_code(response.code.to_i)
|
83
|
+
url = request_model.get_url + request_model.get_path
|
84
|
+
uri = URI(url)
|
85
|
+
headers = request_model.get_headers
|
86
|
+
body = JSON.dump(request_model.get_body)
|
87
|
+
|
88
|
+
request = Net::HTTP::Post.new(uri, headers)
|
89
|
+
request.body = body
|
83
90
|
|
84
|
-
|
85
|
-
|
91
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }
|
92
|
+
|
93
|
+
response_model = ResponseModel.new
|
94
|
+
response_model.set_status_code(response.code.to_i)
|
95
|
+
|
96
|
+
# Check if the response body is empty or invalid before parsing
|
97
|
+
if response.is_a?(Net::HTTPSuccess) && !response.body.strip.empty?
|
98
|
+
# Check if the response body is JSON
|
99
|
+
content_type = response['Content-Type']&.downcase
|
100
|
+
if content_type&.include?('application/json')
|
86
101
|
begin
|
87
102
|
parsed_data = JSON.parse(response.body)
|
88
103
|
response_model.set_data(parsed_data)
|
@@ -91,17 +106,16 @@ class NetworkClient
|
|
91
106
|
response_model.set_error("Invalid JSON response: #{e.message}")
|
92
107
|
end
|
93
108
|
else
|
109
|
+
response_model.set_data(response.body)
|
94
110
|
end
|
95
|
-
rescue StandardError => e
|
96
|
-
LoggerService.log(LogLevelEnum::ERROR, "POST request failed: #{e.message}", nil)
|
97
111
|
end
|
98
112
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
113
|
+
response_model
|
114
|
+
rescue StandardError => e
|
115
|
+
LoggerService.log(LogLevelEnum::ERROR, "POST request failed: #{e.message}", nil)
|
116
|
+
response_model = ResponseModel.new
|
117
|
+
response_model.set_error(e.message)
|
118
|
+
response_model
|
105
119
|
end
|
106
120
|
|
107
121
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2025 Wingify Software Pvt. Ltd.
|
1
|
+
# Copyright 2024-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.
|
@@ -69,10 +69,12 @@ class NetworkManager
|
|
69
69
|
begin
|
70
70
|
network_options = create_request(request)
|
71
71
|
raise 'No URL found' if network_options.get_url.nil?
|
72
|
-
|
73
|
-
@client.post(network_options)
|
72
|
+
|
73
|
+
response = @client.post(network_options) # Return the response
|
74
|
+
response
|
74
75
|
rescue => e
|
75
76
|
LoggerService.log(LogLevelEnum::ERROR, "Error posting: #{e.message}", nil)
|
77
|
+
return ResponseModel.new.set_error(e.message) # Return error response
|
76
78
|
end
|
77
|
-
end
|
79
|
+
end
|
78
80
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2025 Wingify Software Pvt. Ltd.
|
1
|
+
# Copyright 2024-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.
|
@@ -41,5 +41,13 @@ class ResponseModel
|
|
41
41
|
def set_error(error)
|
42
42
|
@error = error
|
43
43
|
end
|
44
|
+
|
45
|
+
def get_error
|
46
|
+
@error
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_status_code
|
50
|
+
@status_code
|
51
|
+
end
|
44
52
|
end
|
45
53
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2025 Wingify Software Pvt. Ltd.
|
1
|
+
# Copyright 2024-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.
|
@@ -18,9 +18,11 @@ class Storage
|
|
18
18
|
@instance = nil
|
19
19
|
|
20
20
|
attr_reader :connector
|
21
|
+
attr_accessor :is_storage_enabled
|
21
22
|
|
22
23
|
def initialize
|
23
24
|
@connector = nil
|
25
|
+
@is_storage_enabled = false
|
24
26
|
end
|
25
27
|
|
26
28
|
# Attach a connector (can be an instance or a class)
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# Copyright 2024-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 '../utils/data_type_util'
|
16
|
+
require_relative '../services/logger_service'
|
17
|
+
require_relative '../enums/log_level_enum'
|
18
|
+
require_relative '../constants/constants'
|
19
|
+
require_relative '../packages/network_layer/manager/network_manager'
|
20
|
+
require 'concurrent'
|
21
|
+
|
22
|
+
class BatchEventsQueue
|
23
|
+
class << self
|
24
|
+
def instance
|
25
|
+
@instance ||= nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def configure(batch_config)
|
29
|
+
@instance = new(batch_config)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# Initializes a new batch events queue with the specified configuration
|
33
|
+
# @param batch_config Configuration object containing:
|
34
|
+
# - request_time_interval: Time interval between batch requests (in seconds)
|
35
|
+
# - events_per_request: Maximum number of events to include in a single request
|
36
|
+
# - flush_callback: Callback function to execute after flushing events
|
37
|
+
# - dispatcher: Function to handle sending the batched events
|
38
|
+
def initialize(batch_config)
|
39
|
+
@queue = []
|
40
|
+
@batch_config = batch_config
|
41
|
+
@network_client = NetworkManager.instance.get_client
|
42
|
+
|
43
|
+
if DataTypeUtil.is_number(batch_config[:request_time_interval]) && batch_config[:request_time_interval] >= 1
|
44
|
+
@request_time_interval = batch_config[:request_time_interval]
|
45
|
+
else
|
46
|
+
@request_time_interval = Constants::DEFAULT_REQUEST_TIME_INTERVAL
|
47
|
+
LoggerService.log(LogLevelEnum::INFO, "EVENT_BATCH_DEFAULTS", {
|
48
|
+
parameter: 'request_time_interval',
|
49
|
+
minLimit: 0,
|
50
|
+
defaultValue: "#{@request_time_interval} seconds"
|
51
|
+
})
|
52
|
+
end
|
53
|
+
|
54
|
+
if DataTypeUtil.is_number(batch_config[:events_per_request]) &&
|
55
|
+
batch_config[:events_per_request] > 0 &&
|
56
|
+
batch_config[:events_per_request] <= Constants::MAX_EVENTS_PER_REQUEST
|
57
|
+
@events_per_request = batch_config[:events_per_request]
|
58
|
+
elsif DataTypeUtil.is_number(batch_config[:events_per_request]) &&
|
59
|
+
batch_config[:events_per_request] > Constants::MAX_EVENTS_PER_REQUEST
|
60
|
+
@events_per_request = Constants::MAX_EVENTS_PER_REQUEST
|
61
|
+
LoggerService.log(LogLevelEnum::INFO, "EVENT_BATCH_MAX_LIMIT", {
|
62
|
+
parameter: 'events_per_request',
|
63
|
+
maxLimit: Constants::MAX_EVENTS_PER_REQUEST.to_s
|
64
|
+
})
|
65
|
+
else
|
66
|
+
@events_per_request = Constants::DEFAULT_EVENTS_PER_REQUEST
|
67
|
+
LoggerService.log(LogLevelEnum::INFO, "EVENT_BATCH_DEFAULTS", {
|
68
|
+
parameter: 'events_per_request',
|
69
|
+
minLimit: 0,
|
70
|
+
defaultValue: @events_per_request.to_s
|
71
|
+
})
|
72
|
+
end
|
73
|
+
|
74
|
+
@flush_callback = batch_config[:flush_callback] if batch_config[:flush_callback].respond_to?(:call)
|
75
|
+
|
76
|
+
@dispatcher = batch_config[:dispatcher]
|
77
|
+
@batch_lock = Mutex.new
|
78
|
+
@timer = nil
|
79
|
+
create_new_batch_timer
|
80
|
+
end
|
81
|
+
|
82
|
+
# Creates a new timer thread to automatically flush events after request_time_interval
|
83
|
+
# The timer is only created if one doesn't already exist
|
84
|
+
def create_new_batch_timer
|
85
|
+
return if @timer
|
86
|
+
|
87
|
+
@timer = Time.now + @request_time_interval
|
88
|
+
@thread = Thread.new { flush_when_request_times_up }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Adds a new event to the queue and manages batch processing
|
92
|
+
# If queue reaches events_per_request limit, it triggers an immediate flush
|
93
|
+
# @param event The event to be added to the queue
|
94
|
+
def enqueue(event)
|
95
|
+
@queue.push(event)
|
96
|
+
|
97
|
+
LoggerService.log(LogLevelEnum::INFO, "EVENT_QUEUE", {
|
98
|
+
queueType: 'batch',
|
99
|
+
event: event.to_json
|
100
|
+
})
|
101
|
+
|
102
|
+
# if the number of events in the queue is equal to the events_per_request, flush
|
103
|
+
if @queue.length >= @events_per_request
|
104
|
+
flush
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Background thread function that monitors the timer
|
109
|
+
# When the timer expires, it flushes the queue and cleans up
|
110
|
+
def flush_when_request_times_up
|
111
|
+
sleep(1) while @timer && Time.now < @timer
|
112
|
+
flush
|
113
|
+
end
|
114
|
+
|
115
|
+
# Processes and sends all queued events
|
116
|
+
# @param manual Boolean indicating if flush was triggered manually
|
117
|
+
# Clears the queue after successful processing
|
118
|
+
def flush(manual = false)
|
119
|
+
@batch_lock.synchronize do
|
120
|
+
if @queue.any?
|
121
|
+
LoggerService.log(LogLevelEnum::DEBUG, "EVENT_BATCH_BEFORE_FLUSHING", {
|
122
|
+
manually: manual ? 'manually' : '',
|
123
|
+
length: @queue.length,
|
124
|
+
accountId: @batch_config[:account_id],
|
125
|
+
timer: manual ? 'Timer will be cleared and registered again' : ''
|
126
|
+
})
|
127
|
+
|
128
|
+
# add events to another queue
|
129
|
+
temp_queue = @queue.dup
|
130
|
+
@queue = []
|
131
|
+
|
132
|
+
if manual
|
133
|
+
future = Concurrent::Future.new(executor: @network_client.get_thread_pool) do
|
134
|
+
handle_flush_response(temp_queue, manual)
|
135
|
+
end
|
136
|
+
future.execute
|
137
|
+
@response = future.value
|
138
|
+
else
|
139
|
+
@network_client.get_thread_pool.post do
|
140
|
+
handle_flush_response(temp_queue, manual)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
else
|
144
|
+
LoggerService.log(LogLevelEnum::DEBUG, "BATCH_QUEUE_EMPTY")
|
145
|
+
@response = {status: "success", events: []}
|
146
|
+
end
|
147
|
+
kill_old_thread if !manual && @thread
|
148
|
+
clear_request_timer
|
149
|
+
create_new_batch_timer
|
150
|
+
@response
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def handle_flush_response(temp_queue, manual)
|
157
|
+
@response = @dispatcher.call(temp_queue, @flush_callback)
|
158
|
+
if @response[:status] == "success"
|
159
|
+
LoggerService.log(LogLevelEnum::INFO, "EVENT_BATCH_After_FLUSHING", {
|
160
|
+
manually: manual ? 'manually' : '',
|
161
|
+
length: temp_queue.length
|
162
|
+
})
|
163
|
+
else
|
164
|
+
@queue.concat(temp_queue)
|
165
|
+
end
|
166
|
+
temp_queue = []
|
167
|
+
@response
|
168
|
+
end
|
169
|
+
|
170
|
+
# Resets the request timer to nil
|
171
|
+
def clear_request_timer
|
172
|
+
@timer = nil
|
173
|
+
end
|
174
|
+
|
175
|
+
def kill_old_thread
|
176
|
+
@old_thread&.kill
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2025 Wingify Software Pvt. Ltd.
|
1
|
+
# Copyright 2024-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.
|
@@ -23,7 +23,7 @@ require_relative '../enums/log_level_enum'
|
|
23
23
|
require_relative '../models/schemas/settings_schema_validation'
|
24
24
|
|
25
25
|
class SettingsService
|
26
|
-
attr_accessor :sdk_key, :account_id, :expiry, :network_timeout, :hostname, :port, :protocol, :is_gateway_service_provided
|
26
|
+
attr_accessor :sdk_key, :account_id, :expiry, :network_timeout, :hostname, :port, :protocol, :is_gateway_service_provided, :is_settings_valid
|
27
27
|
|
28
28
|
class << self
|
29
29
|
attr_accessor :instance
|
@@ -38,6 +38,7 @@ class SettingsService
|
|
38
38
|
@account_id = options[:account_id]
|
39
39
|
@expiry = options.dig(:settings, :expiry) || Constants::SETTINGS_EXPIRY
|
40
40
|
@network_timeout = options.dig(:settings, :timeout) || Constants::SETTINGS_TIMEOUT
|
41
|
+
@is_settings_valid = false
|
41
42
|
|
42
43
|
if options[:gateway_service] && options[:gateway_service][:url]
|
43
44
|
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]}")
|
@@ -109,6 +110,7 @@ class SettingsService
|
|
109
110
|
settings = fetch_settings_and_cache_in_storage
|
110
111
|
is_valid = SettingsSchema.new.is_settings_valid(settings)
|
111
112
|
if is_valid
|
113
|
+
@is_settings_valid = true
|
112
114
|
LoggerService.log(LogLevelEnum::INFO, "SETTINGS_FETCH_SUCCESS")
|
113
115
|
settings
|
114
116
|
else
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# Copyright 2024-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 './network_util'
|
16
|
+
require_relative '../enums/http_method_enum'
|
17
|
+
require_relative '../enums/url_enum'
|
18
|
+
require_relative '../enums/log_level_enum'
|
19
|
+
require_relative '../services/logger_service'
|
20
|
+
require_relative '../packages/network_layer/manager/network_manager'
|
21
|
+
require_relative '../packages/network_layer/models/request_model'
|
22
|
+
|
23
|
+
class BatchEventDispatcher
|
24
|
+
|
25
|
+
class << self
|
26
|
+
|
27
|
+
# Dispatches a batch of events to the VWO server
|
28
|
+
# @param properties [Hash] The event properties to send
|
29
|
+
# @param callback [Proc] Optional callback function to execute after the request (defaults to empty proc)
|
30
|
+
# @param query_params [Hash] Query parameters to include in the request
|
31
|
+
def dispatch(properties, callback = -> {}, query_params)
|
32
|
+
# Send the prepared payload via POST API request
|
33
|
+
send_batch_post_api_request(query_params, properties, callback)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sends a POST API request with given properties and payload
|
37
|
+
def send_batch_post_api_request(properties, payload, callback)
|
38
|
+
network_instance = NetworkManager.instance
|
39
|
+
headers = {}
|
40
|
+
headers['Authorization'] = "#{SettingsService.instance.sdk_key}"
|
41
|
+
|
42
|
+
request = RequestModel.new(
|
43
|
+
UrlUtil.get_base_url,
|
44
|
+
HttpMethodEnum::POST,
|
45
|
+
UrlEnum::BATCH_EVENTS,
|
46
|
+
properties,
|
47
|
+
payload,
|
48
|
+
headers,
|
49
|
+
SettingsService.instance.protocol,
|
50
|
+
SettingsService.instance.port
|
51
|
+
)
|
52
|
+
|
53
|
+
begin
|
54
|
+
response = network_instance.post(request)
|
55
|
+
handle_batch_response(UrlEnum::BATCH_EVENTS, payload, properties, response, response.get_data, callback)
|
56
|
+
rescue StandardError => err
|
57
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
58
|
+
method: "#{HttpMethodEnum::POST} #{UrlEnum::BATCH_EVENTS}",
|
59
|
+
err: err.is_a?(Hash) ? err.to_json : err
|
60
|
+
})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Handles the response from a batch event API call
|
65
|
+
# @param end_point [String] The API endpoint that was called
|
66
|
+
# @param payload [Hash] The payload that was sent in the request
|
67
|
+
# @param query_params [Hash] The query parameters used in the request
|
68
|
+
# @param res [ResponseModel] The response object from the API call
|
69
|
+
# @param raw_data [String] The raw response data from the API
|
70
|
+
# @param callback [Proc] Optional callback to be executed after handling the response
|
71
|
+
def handle_batch_response(end_point, payload, query_params, res, raw_data, callback)
|
72
|
+
events_per_request = payload[:ev].length
|
73
|
+
account_id = query_params[:a]
|
74
|
+
|
75
|
+
error = res.get_error
|
76
|
+
if error
|
77
|
+
LoggerService.log(LogLevelEnum::INFO, "IMPRESSION_BATCH_FAILED")
|
78
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
79
|
+
method: "#{HttpMethodEnum::POST} #{UrlEnum::BATCH_EVENTS}",
|
80
|
+
err: error
|
81
|
+
})
|
82
|
+
callback.call(error, payload.to_json) if callback.respond_to?(:call)
|
83
|
+
return {status: "error", events: payload}
|
84
|
+
else
|
85
|
+
case res.get_status_code
|
86
|
+
when 200
|
87
|
+
LoggerService.log(LogLevelEnum::INFO, "IMPRESSION_BATCH_SUCCESS", {
|
88
|
+
accountId: account_id,
|
89
|
+
endPoint: end_point,
|
90
|
+
})
|
91
|
+
callback.call(nil, payload.to_json) if callback.respond_to?(:call)
|
92
|
+
return {status: "success", events: payload}
|
93
|
+
when 413
|
94
|
+
LoggerService.log(LogLevelEnum::DEBUG, "CONFIG_BATCH_EVENT_LIMIT_EXCEEDED", {
|
95
|
+
accountId: account_id,
|
96
|
+
endPoint: end_point,
|
97
|
+
eventsPerRequest: events_per_request
|
98
|
+
})
|
99
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
100
|
+
method: "#{HttpMethodEnum::POST} #{UrlEnum::BATCH_EVENTS}",
|
101
|
+
err: error
|
102
|
+
})
|
103
|
+
callback.call(error, payload.to_json) if callback.respond_to?(:call)
|
104
|
+
return {status: "error", events: payload}
|
105
|
+
else
|
106
|
+
LoggerService.log(LogLevelEnum::INFO, "IMPRESSION_BATCH_FAILED")
|
107
|
+
LoggerService.log(LogLevelEnum::ERROR, "NETWORK_CALL_FAILED", {
|
108
|
+
method: "#{HttpMethodEnum::POST} #{UrlEnum::BATCH_EVENTS}",
|
109
|
+
err: error
|
110
|
+
})
|
111
|
+
callback.call(error, payload.to_json) if callback.respond_to?(:call)
|
112
|
+
return {status: "error", events: payload}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2025 Wingify Software Pvt. Ltd.
|
1
|
+
# Copyright 2024-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.
|
@@ -17,6 +17,10 @@ require_relative '../models/campaign/campaign_model'
|
|
17
17
|
require_relative '../models/campaign/feature_model'
|
18
18
|
require_relative '../models/settings/settings_model'
|
19
19
|
require_relative '../utils/data_type_util'
|
20
|
+
require_relative '../services/logger_service'
|
21
|
+
require_relative '../enums/log_level_enum'
|
22
|
+
require_relative '../constants/constants'
|
23
|
+
require_relative '../utils/data_type_util'
|
20
24
|
|
21
25
|
# Clones an object deeply.
|
22
26
|
# @param obj [Object] The object to clone.
|