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.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/lib/resources/debug_messages.json +21 -0
  3. data/lib/resources/error_messages.json +63 -0
  4. data/lib/resources/info_messages.json +43 -0
  5. data/lib/resources/warn_messages.json +1 -0
  6. data/lib/vwo.rb +40 -0
  7. data/lib/wingify/api/get_flag.rb +244 -0
  8. data/lib/wingify/api/set_attribute.rb +57 -0
  9. data/lib/wingify/api/track_event.rb +80 -0
  10. data/lib/wingify/constants/constants.rb +106 -0
  11. data/lib/wingify/decorators/storage_decorator.rb +82 -0
  12. data/lib/wingify/enums/api_enum.rb +23 -0
  13. data/lib/wingify/enums/campaign_type_enum.rb +19 -0
  14. data/lib/wingify/enums/debug_category_enum.rb +20 -0
  15. data/lib/wingify/enums/decision_types_enum.rb +18 -0
  16. data/lib/wingify/enums/event_enum.rb +22 -0
  17. data/lib/wingify/enums/headers_enum.rb +20 -0
  18. data/lib/wingify/enums/hooks_enum.rb +17 -0
  19. data/lib/wingify/enums/http_method_enum.rb +18 -0
  20. data/lib/wingify/enums/log_level_enum.rb +21 -0
  21. data/lib/wingify/enums/log_level_to_number.rb +27 -0
  22. data/lib/wingify/enums/status_enum.rb +19 -0
  23. data/lib/wingify/enums/storage_enum.rb +22 -0
  24. data/lib/wingify/enums/url_enum.rb +22 -0
  25. data/lib/wingify/models/campaign/campaign_model.rb +192 -0
  26. data/lib/wingify/models/campaign/feature_model.rb +111 -0
  27. data/lib/wingify/models/campaign/impact_campaign_model.rb +38 -0
  28. data/lib/wingify/models/campaign/metric_model.rb +44 -0
  29. data/lib/wingify/models/campaign/rule_model.rb +56 -0
  30. data/lib/wingify/models/campaign/variable_model.rb +51 -0
  31. data/lib/wingify/models/campaign/variation_model.rb +137 -0
  32. data/lib/wingify/models/gateway_service_model.rb +39 -0
  33. data/lib/wingify/models/schemas/settings_schema_validation.rb +104 -0
  34. data/lib/wingify/models/settings/settings_model.rb +101 -0
  35. data/lib/wingify/models/storage/storage_data_model.rb +44 -0
  36. data/lib/wingify/models/user/context_model.rb +154 -0
  37. data/lib/wingify/models/user/context_vwo_model.rb +38 -0
  38. data/lib/wingify/models/user/get_flag_response.rb +50 -0
  39. data/lib/wingify/models/vwo_options_model.rb +129 -0
  40. data/lib/wingify/packages/decision_maker/decision_maker.rb +60 -0
  41. data/lib/wingify/packages/logger/core/log_manager.rb +92 -0
  42. data/lib/wingify/packages/logger/core/transport_manager.rb +76 -0
  43. data/lib/wingify/packages/logger/log_message_builder.rb +70 -0
  44. data/lib/wingify/packages/logger/logger.rb +38 -0
  45. data/lib/wingify/packages/logger/transports/console_transport.rb +49 -0
  46. data/lib/wingify/packages/network_layer/client/network_client.rb +276 -0
  47. data/lib/wingify/packages/network_layer/handlers/request_handler.rb +37 -0
  48. data/lib/wingify/packages/network_layer/manager/network_manager.rb +145 -0
  49. data/lib/wingify/packages/network_layer/models/global_request_model.rb +105 -0
  50. data/lib/wingify/packages/network_layer/models/request_model.rb +179 -0
  51. data/lib/wingify/packages/network_layer/models/response_model.rb +62 -0
  52. data/lib/wingify/packages/segmentation_evaluator/core/segmentation_manager.rb +77 -0
  53. data/lib/wingify/packages/segmentation_evaluator/enums/segment_operand_regex_enum.rb +29 -0
  54. data/lib/wingify/packages/segmentation_evaluator/enums/segment_operand_value_enum.rb +26 -0
  55. data/lib/wingify/packages/segmentation_evaluator/enums/segment_operator_value_enum.rb +33 -0
  56. data/lib/wingify/packages/segmentation_evaluator/evaluators/segment_evaluator.rb +218 -0
  57. data/lib/wingify/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.rb +415 -0
  58. data/lib/wingify/packages/segmentation_evaluator/utils/segment_util.rb +44 -0
  59. data/lib/wingify/packages/storage/connector.rb +26 -0
  60. data/lib/wingify/packages/storage/storage.rb +47 -0
  61. data/lib/wingify/services/batch_event_queue.rb +179 -0
  62. data/lib/wingify/services/campaign_decision_service.rb +161 -0
  63. data/lib/wingify/services/hooks_service.rb +51 -0
  64. data/lib/wingify/services/logger_service.rb +114 -0
  65. data/lib/wingify/services/settings_service.rb +178 -0
  66. data/lib/wingify/services/storage_service.rb +66 -0
  67. data/lib/wingify/utils/batch_event_dispatcher_util.rb +178 -0
  68. data/lib/wingify/utils/brand_context.rb +33 -0
  69. data/lib/wingify/utils/brand_util.rb +53 -0
  70. data/lib/wingify/utils/campaign_util.rb +284 -0
  71. data/lib/wingify/utils/data_type_util.rb +105 -0
  72. data/lib/wingify/utils/debugger_service_util.rb +40 -0
  73. data/lib/wingify/utils/decision_util.rb +259 -0
  74. data/lib/wingify/utils/event_util.rb +55 -0
  75. data/lib/wingify/utils/function_util.rb +141 -0
  76. data/lib/wingify/utils/gateway_service_util.rb +101 -0
  77. data/lib/wingify/utils/impression_util.rb +66 -0
  78. data/lib/wingify/utils/log_message_util.rb +42 -0
  79. data/lib/wingify/utils/meg_util.rb +357 -0
  80. data/lib/wingify/utils/network_util.rb +503 -0
  81. data/lib/wingify/utils/rule_evaluation_util.rb +57 -0
  82. data/lib/wingify/utils/settings_util.rb +38 -0
  83. data/lib/wingify/utils/usage_stats_util.rb +119 -0
  84. data/lib/wingify/utils/uuid_util.rb +96 -0
  85. data/lib/wingify/wingify_builder.rb +261 -0
  86. data/lib/wingify/wingify_client.rb +227 -0
  87. data/lib/wingify.rb +117 -0
  88. 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