Top Level Namespace

Defined Under Namespace

Modules: ApiEnum, BrandContext, BrandUtil, CampaignTypeEnum, CampaignUtil, Constants, DataTypeUtil, DebugCategoryEnum, DecisionTypes, EventEnum, HeadersEnum, HooksEnum, HttpMethodEnum, LogLevelEnum, LogLevelToNumber, SegmentOperandRegexEnum, SegmentOperandValueEnum, SegmentOperatorValueEnum, StatusEnum, StorageEnum, UrlEnum, VWO, Wingify Classes: BatchEventDispatcherUtil, BatchEventsQueue, CampaignDecisionService, CampaignModel, Connector, ConsoleTransport, ContextModel, ContextVWOModel, DebuggerServiceUtil, DecisionMaker, DecisionUtil, FeatureModel, FlagApi, GatewayServiceModel, GetFlagResponse, GlobalRequestModel, HooksService, ImpactCampaignModel, LogManager, LogMessageBuilder, LogTransportManager, LoggerService, MetricModel, NetworkClient, NetworkManager, NetworkUtil, RequestHandler, RequestModel, ResponseModel, RuleModel, SegmentEvaluator, SegmentOperandEvaluator, SegmentationManager, SetAttributeApi, SettingsModel, SettingsSchema, SettingsService, Storage, StorageDataModel, StorageDecorator, StorageService, TrackApi, UUIDUtil, UsageStatsUtil, VWOLogger, VWOOptionsModel, VariableModel, VariationModel, WingifyBuilder, WingifyClient

Constant Summary collapse

VWOClient =

Map existing VWO classes to Wingify aliases to maintain 100% backward compatibility

WingifyClient
VWOBuilder =
WingifyBuilder

Instance Method Summary collapse

Instance Method Details

#add_is_gateway_service_required_flag(settings) ⇒ Object

Adds isGatewayServiceRequired flag to each feature in the settings based on pre-segmentation.

Parameters:



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/wingify/utils/gateway_service_util.rb', line 72

def add_is_gateway_service_required_flag(settings)
  pattern = /
    (?!custom_variable\s*:\s*{\s*"name"\s*:\s*")   # Prevent matching inside custom_variable
    \b(country|region|city|os|device|device_type|browser_string|ua|browser_version|os_version)\b
    |
    "custom_variable"\s*:\s*{\s*"name"\s*:\s*"inlist\([^)]*\)"
  /x

  settings.get_features.each do |feature|
    feature.get_rules_linked_campaign.each do |rule|
      segments = {}

      if [CampaignTypeEnum::PERSONALIZE, CampaignTypeEnum::ROLLOUT].include?(rule.get_type)
        segments = rule.get_variations[0].get_segments
      else
        segments = rule.get_segments
      end

      next unless segments

      json_segments = segments.to_json
      matches = json_segments.scan(pattern)

      if matches.any?
        feature.set_is_gateway_service_required(true)
        break
      end
    end
  end
end

#add_linked_campaigns_to_settings(settings) ⇒ Object

Adds linked campaigns to each feature in the settings based on rules.

Parameters:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/wingify/utils/function_util.rb', line 105

def add_linked_campaigns_to_settings(settings)
  campaign_map = settings.get_campaigns.each_with_object({}) do |campaign, map|
    map[campaign.get_id] = campaign
  end

  settings.get_features.each do |feature|
    rules_linked_campaign = feature.get_rules.map do |rule|
      campaign = campaign_map[rule.get_campaign_id]
      next unless campaign

      linked_campaign = campaign.clone
      linked_campaign.set_rule_key(rule.get_rule_key)
      if rule.get_variation_id
        variation = campaign.get_variations.find { |v| v.get_id == rule.get_variation_id }
        linked_campaign.set_variations([variation]) if variation
      end

      linked_campaign
    end.compact

    feature.set_rules_linked_campaign(rules_linked_campaign)
  end
end

#build_message(template, data = {}) ⇒ String

Constructs a message by replacing placeholders in a template with corresponding values from a data hash.

Parameters:

  • template (String)

    The message template containing placeholders in the format ‘key`.

  • data (Hash) (defaults to: {})

    An object containing keys and values used to replace the placeholders in the template.

Returns:

  • (String)

    The constructed message with all placeholders replaced by their corresponding values from the data hash.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/wingify/utils/log_message_util.rb', line 20

def build_message(template, data = {})
  begin
    template.gsub(/\{([0-9a-zA-Z_]+)\}/) do |match|
      key = match.tr('{}', '') # Extract the key from `{key}`
      
      # Check for escaped placeholders like `{{key}}`
      if template[template.index(match) - 1] == '{' && template[template.index(match) + match.length] == '}'
        key
      else
        value = data[key.to_sym] || data[key] # Support both string and symbol keys

        # Return empty string if key is missing
        next '' if value.nil?

        # If value is a callable (Proc/Lambda), execute it
        value.respond_to?(:call) ? value.call : value
      end
    end
  rescue StandardError
    template # Return the original template in case of an error
  end
end

#clone_object(obj) ⇒ Object

Clones an object deeply.

Parameters:

  • obj (Object)

    The object to clone.

Returns:

  • (Object)

    The cloned object.



28
29
30
31
# File 'lib/wingify/utils/function_util.rb', line 28

def clone_object(obj)
  return obj unless obj
  Marshal.load(Marshal.dump(obj))
end

#create_and_send_impression_for_variation_shown(settings, campaign_id, variation_id, context, feature_key) ⇒ Object

Parameters:

  • context (ContextModel)

    The user context model containing user-specific data.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/wingify/utils/impression_util.rb', line 29

def create_and_send_impression_for_variation_shown(settings, campaign_id, variation_id, context, feature_key)
  # Get base properties for the event
  properties = NetworkUtil.get_events_base_properties(
    EventEnum::VARIATION_SHOWN,
    URI.encode_www_form_component(context.get_user_agent), # Encode user agent for URL safety
    context.get_ip_address
  )

  # Construct payload data for tracking the user
  payload = NetworkUtil.get_track_user_payload_data(
    EventEnum::VARIATION_SHOWN,
    campaign_id,
    variation_id,
    context
  )

  campaign_key_with_feature_name = CampaignUtil.get_campaign_key_from_campaign_id(settings, campaign_id)
  variation_name = CampaignUtil.get_variation_name_from_campaign_id_and_variation_id(settings, campaign_id, variation_id)

  campaign_key = ''

  if feature_key == campaign_key_with_feature_name
    campaign_key = Constants::IMPACT_ANALYSIS
  else
    campaign_key = campaign_key_with_feature_name.split("#{feature_key}_")[1]
  end

  campaign_type = CampaignUtil.get_campaign_type_from_campaign_id(settings, campaign_id)

  # check if batching is enabled
  if BatchEventsQueue.instance
    # add the payload to the batch events queue
    BatchEventsQueue.instance.enqueue(payload)
  else
    # Send the constructed payload via POST request
    NetworkUtil.send_post_api_request(properties, payload, { campaign_type: campaign_type, feature_key: feature_key, campaign_key: campaign_key, variation_name: variation_name })
  end
end

#does_event_belong_to_any_feature(event_name, settings) ⇒ Boolean

Checks if an event exists within any feature’s metrics.

Parameters:

  • event_name (String)

    The name of the event to check.

  • settings (SettingsModel)

    The settings containing features.

Returns:

  • (Boolean)

    True if the event exists, otherwise false.



97
98
99
100
101
# File 'lib/wingify/utils/function_util.rb', line 97

def does_event_belong_to_any_feature(event_name, settings)
  settings.get_features.any? do |feature|
    feature.get_metrics.any? { |metric| metric.get_identifier == event_name }
  end
end

#evaluate_groups(settings, feature, group_id, evaluated_feature_map, context, storage_service) ⇒ Object

Evaluates groups for a given feature and group ID.

Parameters:

  • settings (SettingsModel)

    The settings for the VWO instance

  • feature (FeatureModel)

    The feature to evaluate

  • group_id (String)

    The group ID to evaluate

  • evaluated_feature_map (Hash)

    The map of evaluated features

  • context (ContextModel)

    The context for the evaluation

  • storage_service (StorageService)

    The storage service for the evaluation



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/wingify/utils/meg_util.rb', line 42

def evaluate_groups(settings, feature, group_id, evaluated_feature_map, context, storage_service)
  feature_to_skip = []
  campaign_map = {}

  # Get all feature keys and campaignIds from the groupId
  feature_keys, group_campaign_ids = get_feature_keys_from_group(settings, group_id)

  feature_keys.each do |feature_key|
    temp_feature = get_feature_from_key(settings, feature_key)

    next if feature_to_skip.include?(feature_key)

    # Evaluate the feature rollout rules
    is_rollout_rule_passed = is_rollout_rule_for_feature_passed(settings, temp_feature, evaluated_feature_map, feature_to_skip, storage_service, context)
    if is_rollout_rule_passed
      settings.get_features.each do |current_feature|
        if current_feature.key == feature_key
          current_feature.get_rules_linked_campaign.each do |rule|
            if group_campaign_ids.include?(rule.id.to_s) || group_campaign_ids.include?("#{rule.id}_#{rule.variations[0].id}")
              campaign_map[feature_key] ||= []
              # Check if the campaign is already present in the campaignMap for the feature
              if campaign_map[feature_key].find_index { |item| item.rule_key == rule.rule_key }.nil?
                campaign_map[feature_key] << rule
              end
            end
          end
        end
      end
    end
  end

  eligible_campaigns, eligible_campaigns_with_storage = get_eligible_campaigns(settings, campaign_map, context, storage_service)

  find_winner_campaign_among_eligible_campaigns(settings, feature.key, eligible_campaigns, eligible_campaigns_with_storage, group_id, context, storage_service)
end

#evaluate_rule(settings, feature, campaign, context, evaluated_feature_map, meg_group_winner_campaigns, storage_service, decision) ⇒ Hash

Evaluates the rules for a given campaign and feature based on the provided context.

Parameters:

  • settings (SettingsModel)

    The settings configuration for evaluation.

  • feature (FeatureModel)

    The feature being evaluated.

  • campaign (CampaignModel)

    The campaign associated with the feature.

  • context (ContextModel)

    The user context for evaluation.

  • evaluated_feature_map (Hash)

    A hash of evaluated features.

  • meg_group_winner_campaigns (Hash)

    A hash of MEG group winner campaigns.

  • storage_service (StorageService)

    The storage service for persistence.

  • decision (Hash)

    The decision object that will be updated based on the evaluation.

Returns:

  • (Hash)

    A hash containing the result of the pre-segmentation and the whitelisted object.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/wingify/utils/rule_evaluation_util.rb', line 36

def evaluate_rule(settings, feature, campaign, context, evaluated_feature_map, meg_group_winner_campaigns, storage_service, decision)
  # Perform whitelisting and pre-segmentation checks
  pre_segmentation_result, whitelisted_object = DecisionUtil.check_whitelisting_and_pre_seg(
    settings, feature, campaign, context, evaluated_feature_map, meg_group_winner_campaigns, storage_service, decision
  )

  # If pre-segmentation is successful and a whitelisted object exists, proceed to send an impression
  if pre_segmentation_result && whitelisted_object.is_a?(Hash) && !whitelisted_object.empty?
    # Update the decision object with campaign and variation details
    decision.merge!(
      experiment_id: campaign.get_id,
      experiment_key: campaign.get_key,
      experiment_variation_id: whitelisted_object[:variation_id]
    )

    # Send an impression for the variation shown
    create_and_send_impression_for_variation_shown(settings, campaign.get_id, whitelisted_object[:variation_id], context, feature.get_key)
  end

  # Return the results of the evaluation
  { pre_segmentation_result: pre_segmentation_result, whitelisted_object: whitelisted_object, updated_decision: decision }
end

#find_winner_campaign_among_eligible_campaigns(settings, feature_key, eligible_campaigns, eligible_campaigns_with_storage, group_id, context, storage_service) ⇒ Object

Find the winner campaign among the eligible campaigns

Parameters:

  • settings (SettingsModel)

    The settings for the VWO instance

  • feature_key (String)

    The key of the feature

  • eligible_campaigns (Array)

    The list of eligible campaigns

  • eligible_campaigns_with_storage (Array)

    The list of eligible campaigns with storage

  • group_id (String)

    The ID of the group



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/wingify/utils/meg_util.rb', line 183

def find_winner_campaign_among_eligible_campaigns(settings, feature_key, eligible_campaigns, eligible_campaigns_with_storage, group_id, context, storage_service)
  winner_campaign = nil
  campaign_ids = CampaignUtil.get_campaign_ids_from_feature_key(settings, feature_key)
  meg_algo_number = settings.get_groups[group_id.to_s][:et.to_s] || Constants::RANDOM_ALGO

  # Check eligible_campaigns_with_storage first
  if eligible_campaigns_with_storage.length == 1
    winner_campaign = eligible_campaigns_with_storage[0]
    LoggerService.log(LogLevelEnum::INFO, "MEG_WINNER_CAMPAIGN", { campaignKey: winner_campaign.key, groupId: group_id, userId: context.id, algo: 'using random algorithm' })
  elsif eligible_campaigns_with_storage.length > 1 && meg_algo_number == Constants::RANDOM_ALGO
    winner_campaign = normalize_weights_and_find_winning_campaign(eligible_campaigns_with_storage, context, campaign_ids, group_id, storage_service)
  elsif eligible_campaigns_with_storage.length > 1
    winner_campaign = get_campaign_using_advanced_algo(settings, eligible_campaigns_with_storage, context, campaign_ids, group_id, storage_service)
  end

  # Fallback to eligible_campaigns if no winner found in storage
  if eligible_campaigns_with_storage.empty?
    if eligible_campaigns.length == 1
      winner_campaign = eligible_campaigns[0]
      LoggerService.log(LogLevelEnum::INFO, "MEG_WINNER_CAMPAIGN", { campaignKey: winner_campaign.key, groupId: group_id, userId: context.id, algo: 'using random algorithm' })
    elsif eligible_campaigns.length > 1 && meg_algo_number == Constants::RANDOM_ALGO
      winner_campaign = normalize_weights_and_find_winning_campaign(eligible_campaigns, context, campaign_ids, group_id, storage_service)
    elsif eligible_campaigns.length > 1
      winner_campaign = get_campaign_using_advanced_algo(settings, eligible_campaigns, context, campaign_ids, group_id, storage_service)
    else
      LoggerService.log(LogLevelEnum::INFO, "No winner campaign found for MEG group: #{group_id}", nil)
    end
  end

  winner_campaign
end

#get_all_experiment_rules(feature) ⇒ Array

Retrieves all AB and Personalize rules from a feature.

Parameters:

Returns:

  • (Array)

    An array of AB and Personalize rules.



75
76
77
78
79
80
81
# File 'lib/wingify/utils/function_util.rb', line 75

def get_all_experiment_rules(feature)
  return [] unless feature

  feature.get_rules_linked_campaign.select do |rule|
    [CampaignTypeEnum::AB, CampaignTypeEnum::PERSONALIZE].include?(rule.get_type)
  end
end

#get_campaign_using_advanced_algo(settings, shortlisted_campaigns, context, called_campaign_ids, group_id, storage_service) ⇒ Object

Advanced algorithm for campaign selection

Parameters:

  • settings (SettingsModel)

    The settings for the VWO instance

  • shortlisted_campaigns (Array)

    The list of shortlisted campaigns

  • context (ContextModel)

    The context for the evaluation

  • called_campaign_ids (Array)

    The list of called campaign IDs

  • group_id (String)

    The ID of the group

  • storage_service (StorageService)

    The storage service for the evaluation



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/wingify/utils/meg_util.rb', line 283

def get_campaign_using_advanced_algo(settings, shortlisted_campaigns, context, called_campaign_ids, group_id, storage_service)
  winner_campaign = nil
  found = false
  priority_order = settings.get_groups[group_id.to_s][:p.to_s] || []
  weights = settings.get_groups[group_id.to_s][:wt.to_s] || {}

  # Check priority order first
  priority_order.each do |priority|
    shortlisted_campaigns.each do |campaign|
      if campaign.id.to_s == priority.to_s || "#{campaign.id}_#{campaign.variations[0].id}" == priority
        winner_campaign = campaign.clone
        found = true
        break
      end
    end
    break if found
  end

  # If no winner found through priority, try weighted distribution
  if winner_campaign.nil?
    participating_campaign_list = shortlisted_campaigns.map do |campaign|
      campaign_id = campaign.id.to_s
      weight = weights[campaign_id] || weights["#{campaign_id}_#{campaign.variations[0].id}"]
      next nil unless weight

      cloned_campaign = campaign.clone
      variation = VariationModel.new.model_from_dictionary(cloned_campaign)
      variation.weight = weight
      variation
    end.compact  # Remove nil values

    # Convert to VariationModel and set allocations
    CampaignUtil.set_campaign_allocation(participating_campaign_list)
    winner_campaign = CampaignDecisionService.new.get_variation(
      participating_campaign_list,
      DecisionMaker.new.calculate_bucket_value(CampaignUtil.get_bucketing_seed(context.get_bucketing_seed || context.get_id, nil, group_id))
    )
  end

  if winner_campaign
    campaign_key = winner_campaign.type == CampaignTypeEnum::AB ? 
      winner_campaign.key : 
      "#{winner_campaign.key}_#{winner_campaign.rule_key}"
    
    LoggerService.log(
      LogLevelEnum::INFO,
      "MEG_WINNER_CAMPAIGN",
      {
        campaignKey: campaign_key,
        groupId: group_id,
        userId: context.id,
        algo: 'using advanced algorithm'
      }
    )
    begin
      StorageDecorator.new.set_data_in_storage(
        {
          feature_key: "#{Constants::VWO_META_MEG_KEY}#{group_id}",
          context: context,
          experiment_id: winner_campaign.id,
          experiment_key: winner_campaign.key,
          experiment_variation_id: winner_campaign.type == CampaignTypeEnum::PERSONALIZE ? winner_campaign.variations[0].id : -1
        },
        storage_service
      )
    rescue StandardError => e
      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})
    end
    return winner_campaign if called_campaign_ids.include?(winner_campaign.id)
  else
    LoggerService.log(LogLevelEnum::INFO, "No winner campaign found for MEG group: #{group_id}, using advanced algorithm", nil)
  end

  nil
end

#get_current_timeString

Gets the current time in ISO string format.

Returns:

  • (String)

    The current time in ISO string format.



35
36
37
# File 'lib/wingify/utils/function_util.rb', line 35

def get_current_time
  Time.now.utc.iso8601
end

#get_current_unix_timestampInteger

Gets the current Unix timestamp in seconds.

Returns:

  • (Integer)

    The current Unix timestamp.



41
42
43
# File 'lib/wingify/utils/function_util.rb', line 41

def get_current_unix_timestamp
  Time.now.to_i
end

#get_current_unix_timestamp_in_millisInteger

Gets the current Unix timestamp in milliseconds.

Returns:

  • (Integer)

    The current Unix timestamp in milliseconds.



47
48
49
# File 'lib/wingify/utils/function_util.rb', line 47

def get_current_unix_timestamp_in_millis
  (Time.now.to_f * 1000).to_i
end

#get_eligible_campaigns(settings, campaign_map, context, storage_service) ⇒ Object

Get the eligible campaigns

Parameters:

  • settings (SettingsModel)

    The settings for the VWO instance

  • campaign_map (Hash)

    The map of campaigns

  • context (ContextModel)

    The context for the evaluation

  • storage_service (StorageService)

    The storage service for the evaluation



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/wingify/utils/meg_util.rb', line 139

def get_eligible_campaigns(settings, campaign_map, context, storage_service)
  eligible_campaigns = []
  eligible_campaigns_with_storage = []
  ineligible_campaigns = []

  campaign_map.each do |feature_key, campaigns|
    campaigns.each do |campaign|
      stored_data = StorageDecorator.new.get_feature_from_storage(feature_key, context, storage_service)

      if stored_data && stored_data[:experiment_variation_id]
        if stored_data[:experiment_key] == campaign.key
          variation = CampaignUtil.get_variation_from_campaign_key(settings, stored_data[:experiment_key], stored_data[:experiment_variation_id])
          if variation
            LoggerService.log(LogLevelEnum::INFO, "MEG_CAMPAIGN_FOUND_IN_STORAGE", { campaignKey: stored_data[:experiment_key], userId: context.id })

            unless eligible_campaigns_with_storage.any? { |item| item.key == campaign.key }
              eligible_campaigns_with_storage.push(campaign)
            end
            next
          end
        end
      end

      # Check if user is eligible for the campaign
      if CampaignDecisionService.new.get_pre_segmentation_decision(campaign, context) && CampaignDecisionService.new.is_user_part_of_campaign(context, campaign)
        LoggerService.log(LogLevelEnum::INFO, "MEG_CAMPAIGN_ELIGIBLE", { campaignKey: campaign.key, userId: context.id })

        eligible_campaigns.push(campaign)
        next
      end

      ineligible_campaigns.push(campaign)
    end
  end

  return eligible_campaigns, eligible_campaigns_with_storage
end

#get_feature_from_key(settings, feature_key) ⇒ FeatureModel?

Retrieves a feature by its key from the settings.

Parameters:

  • settings (SettingsModel)

    The settings containing features.

  • feature_key (String)

    The key of the feature to find.

Returns:

  • (FeatureModel, nil)

    The feature if found, otherwise nil.



87
88
89
90
91
# File 'lib/wingify/utils/function_util.rb', line 87

def get_feature_from_key(settings, feature_key)
  return nil unless settings&.get_features

  settings.get_features.find { |feature| feature.get_key == feature_key }
end

#get_feature_keys_from_group(settings, group_id) ⇒ Array

Get the feature keys from the group

Parameters:

  • settings (SettingsModel)

    The settings for the VWO instance

  • group_id (String)

    The group ID to get the feature keys from

Returns:

  • (Array)

    The feature keys and the group campaign IDs



82
83
84
85
86
87
# File 'lib/wingify/utils/meg_util.rb', line 82

def get_feature_keys_from_group(settings, group_id)
  group_campaign_ids = CampaignUtil.get_campaigns_by_group_id(settings, group_id)
  feature_keys = CampaignUtil.get_feature_keys_from_campaign_ids(settings, group_campaign_ids)

  return feature_keys, group_campaign_ids
end

#get_formatted_error_message(error) ⇒ String

Formats error messages for logging

Parameters:

  • error (Object)

    The error object (can be Error, String, Hash, etc.)

Returns:

  • (String)

    The formatted error message



132
133
134
135
136
137
138
139
140
141
# File 'lib/wingify/utils/function_util.rb', line 132

def get_formatted_error_message(error)
  if error.is_a?(StandardError)
    error.message
  elsif error.is_a?(String)
    error
  elsif error.is_a?(Hash) || error.is_a?(Array)
    error.to_json
  end
  error
end

#get_from_gateway_service(query_params, endpoint) ⇒ Hash, Boolean

Retrieves data from a web service using the specified query parameters and endpoint.

Parameters:

  • query_params (Hash)

    The parameters to be used in the query string of the request.

  • endpoint (String)

    The endpoint URL to which the request is sent.

Returns:

  • (Hash, Boolean)

    The response data or false if an error occurs.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/wingify/utils/gateway_service_util.rb', line 29

def get_from_gateway_service(query_params, endpoint)
  network_instance = NetworkManager.instance

  unless SettingsService.instance.is_gateway_service_provided
    LoggerService.log(LogLevelEnum::ERROR, "INVALID_GATEWAY_URL", { an: ApiEnum::GET_FLAG})
    return false
  end

  begin
    request = RequestModel.new(
      SettingsService.instance.hostname,
      HttpMethodEnum::GET,
      SettingsService.instance.get_updated_endpoint_with_collection_prefix(endpoint, SettingsService.instance.is_gateway_service_provided),
      query_params,
      nil,
      nil,
      SettingsService.instance.protocol,
      SettingsService.instance.port
    )

    response = network_instance.get(request)
    return response.get_data
  rescue StandardError => e
    LoggerService.log(LogLevelEnum::ERROR, "ERROR_FETCHING_SETTINGS", { err: e.message, an: ApiEnum::GET_FLAG})
    return false
  end
end

#get_key_value(obj) ⇒ Hash?

Extracts the first key-value pair from the provided object.

Parameters:

  • obj (Hash)

    The object from which to extract the key-value pair.

Returns:

  • (Hash, nil)

    A hash containing the first key and value, or nil if input is not a hash.



25
26
27
28
29
30
31
32
# File 'lib/wingify/packages/segmentation_evaluator/utils/segment_util.rb', line 25

def get_key_value(obj)
  return nil unless is_object?(obj)

  key = obj.keys.first
  return nil unless key

  { key: key, value: obj[key] }
end

#get_query_params(query_params) ⇒ Hash

Encodes the query parameters to ensure they are URL-safe.

Parameters:

  • query_params (Hash)

    The query parameters to be encoded.

Returns:

  • (Hash)

    An object containing the encoded query parameters.



60
61
62
63
64
65
66
67
68
# File 'lib/wingify/utils/gateway_service_util.rb', line 60

def get_query_params(query_params)
  encoded_params = {}

  query_params.each do |key, value|
    encoded_params[key] = URI.encode_www_form_component(value.to_s)
  end

  encoded_params
end

#get_random_numberFloat

Generates a random number between 0 and 1.

Returns:

  • (Float)

    A random number.



53
54
55
# File 'lib/wingify/utils/function_util.rb', line 53

def get_random_number
  rand
end

#get_specific_rules_based_on_type(feature, type = nil) ⇒ Array

Retrieves specific rules based on the type from a feature.

Parameters:

Returns:

  • (Array)

    An array of rules that match the type.



61
62
63
64
65
66
67
68
69
70
# File 'lib/wingify/utils/function_util.rb', line 61

def get_specific_rules_based_on_type(feature, type = nil)
  return [] unless feature&.get_rules_linked_campaign

  return feature.get_rules_linked_campaign if type.nil? || !DataTypeUtil.is_string(type)

  feature.get_rules_linked_campaign.select do |rule|
    rule_model = CampaignModel.new.model_from_dictionary(rule)
    rule_model.get_type == type
  end
end

#is_object?(val) ⇒ Boolean

Utility function to check if a value is an object (excluding arrays and other types)

Returns:

  • (Boolean)


18
19
20
# File 'lib/wingify/packages/segmentation_evaluator/utils/segment_util.rb', line 18

def is_object?(val)
  val.is_a?(Hash)
end

#is_rollout_rule_for_feature_passed(settings, feature, evaluated_feature_map, feature_to_skip, storage_service, context) ⇒ Object

Check if the rollout rule for the feature is passed

Parameters:

  • settings (SettingsModel)

    The settings for the VWO instance

  • feature (FeatureModel)

    The feature to check the rollout rule for

  • evaluated_feature_map (Hash)

    The map of evaluated features

  • feature_to_skip (Array)

    The list of features to skip

  • storage_service (StorageService)

    The storage service for the evaluation

  • context (ContextModel)

    The context for the evaluation



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/wingify/utils/meg_util.rb', line 96

def is_rollout_rule_for_feature_passed(settings, feature, evaluated_feature_map, feature_to_skip, storage_service, context)
  return true if evaluated_feature_map.key?(feature.key) && evaluated_feature_map[feature.key].key?(:rollout_id)

  rollout_rules = get_specific_rules_based_on_type(feature, CampaignTypeEnum::ROLLOUT)

  if rollout_rules.any?
    rule_to_test_for_traffic = nil
    rollout_rules.each do |rule|
      result = evaluate_rule(settings, feature, rule, context, evaluated_feature_map, nil, storage_service, {})
      if result[:pre_segmentation_result]
        rule_to_test_for_traffic = rule
        break
      end
    end

    if rule_to_test_for_traffic
      campaign = CampaignModel.new.model_from_dictionary(rule_to_test_for_traffic)
      variation = evaluate_traffic_and_get_variation(settings, campaign, context)
      if variation.is_a?(VariationModel) && !variation.nil? && variation.id.is_a?(Integer)
        evaluated_feature_map[feature.key] = {
          rollout_id: rule_to_test_for_traffic.id,
          rollout_key: rule_to_test_for_traffic.key,
          rollout_variation_id: rule_to_test_for_traffic.variations[0].id
        }
        return true
      end
    end

    # No rollout rule passed
    feature_to_skip.push(feature.key)
    return false
  end

  # No rollout rule, evaluate experiments
  LoggerService.log(LogLevelEnum::INFO, "MEG_SKIP_ROLLOUT_EVALUATE_EXPERIMENTS", { featureKey: feature.key })
  return true
end

#match_with_regex(string, regex) ⇒ Array?

Matches a string against a regular expression and returns the match result.

Parameters:

  • string (String)

    The string to match against the regex.

  • regex (String)

    The regex pattern as a string.

Returns:

  • (Array, nil)

    The results of the regex match, or nil if an error occurs.



38
39
40
41
42
43
44
# File 'lib/wingify/packages/segmentation_evaluator/utils/segment_util.rb', line 38

def match_with_regex(string, regex)
  begin
    return string.match(Regexp.new(regex))
  rescue StandardError
    return nil
  end
end

#normalize_weights_and_find_winning_campaign(shortlisted_campaigns, context, called_campaign_ids, group_id, storage_service) ⇒ Object

Helper for random allocation winner selection

Parameters:

  • shortlisted_campaigns (Array)

    The list of shortlisted campaigns

  • context (ContextModel)

    The context for the evaluation

  • called_campaign_ids (Array)

    The list of called campaign IDs

  • group_id (String)

    The ID of the group

  • storage_service (StorageService)

    The storage service for the evaluation



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/wingify/utils/meg_util.rb', line 221

def normalize_weights_and_find_winning_campaign(shortlisted_campaigns, context, called_campaign_ids, group_id, storage_service)
  # Convert to VariationModel first and then normalize weights
  shortlisted_variations = shortlisted_campaigns.map do |campaign|
    variation = VariationModel.new.model_from_dictionary(campaign)
    variation.weight = (100.0 / shortlisted_campaigns.length).round(4)
    variation
  end

  # Set campaign allocation
  CampaignUtil.set_campaign_allocation(shortlisted_variations)

  winner_campaign = CampaignDecisionService.new.get_variation(
    shortlisted_variations,
    DecisionMaker.new.calculate_bucket_value(CampaignUtil.get_bucketing_seed(context.get_bucketing_seed || context.get_id, nil, group_id))
  )

  if winner_campaign
    campaign_key = winner_campaign.type == CampaignTypeEnum::AB ? 
      winner_campaign.key : 
      "#{winner_campaign.key}_#{winner_campaign.rule_key}"

    LoggerService.log(
      LogLevelEnum::INFO,
      "MEG_WINNER_CAMPAIGN",
      {
        campaignKey: campaign_key,
        groupId: group_id,
        userId: context.id,
        algo: 'using random algorithm'
      }
    )

    begin
      StorageDecorator.new.set_data_in_storage(
        {
          feature_key: "#{Constants::VWO_META_MEG_KEY}#{group_id}",
          context: context,
          experiment_id: winner_campaign.id,
          experiment_key: winner_campaign.key,
          experiment_variation_id: winner_campaign.type == CampaignTypeEnum::PERSONALIZE ? winner_campaign.variations[0].id : -1
        },
        storage_service
      )
    rescue StandardError => e
      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})
    end

    return winner_campaign if called_campaign_ids.include?(winner_campaign.id)
  else
    LoggerService.log(LogLevelEnum::INFO, "No winner campaign found for MEG group: #{group_id}, using random algorithm", nil)
  end

  nil
end

#send_sdk_init_event(settings_fetch_time, sdk_init_time) ⇒ Object

Sends an init called event to VWO. This event is triggered when the init function is called.

Parameters:

  • settings_fetch_time (Number)

    Time taken to fetch settings in milliseconds.

  • sdk_init_time (Number)

    Time taken to initialize the SDK in milliseconds.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/wingify/utils/event_util.rb', line 23

def send_sdk_init_event(settings_fetch_time, sdk_init_time)
  # Get base properties for the event
  properties = NetworkUtil.get_events_base_properties(EventEnum::INIT_CALLED)
  payload = NetworkUtil.get_sdk_init_event_payload(EventEnum::INIT_CALLED, settings_fetch_time, sdk_init_time)

  # check if batching is enabled
  if BatchEventsQueue.instance
    # add the payload to the batch events queue
    BatchEventsQueue.instance.enqueue(payload)
  else
    # Send the constructed payload via POST request
    NetworkUtil.send_event(properties, payload)
  end
end

#send_sdk_usage_stats_event(usage_stats_account_id) ⇒ Object

Sends a usage stats event to VWO.

Parameters:

  • usage_stats_account_id
    • The account id for usage stats.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/wingify/utils/event_util.rb', line 40

def send_sdk_usage_stats_event()
  # create query parameters
  properties = NetworkUtil.get_events_base_properties(EventEnum::USAGE_STATS, nil, nil, true, )

  # create payload
  payload = NetworkUtil.get_sdk_usage_stats_payload_data(EventEnum::USAGE_STATS, )

  # check if batching is enabled
  if BatchEventsQueue.instance
    # add the payload to the batch events queue
    BatchEventsQueue.instance.enqueue(payload)
  else
    # send event
    NetworkUtil.send_event(properties, payload)
  end
end

#set_settings_and_add_campaigns_to_rules(settings, vwo_client_instance) ⇒ Object

Sets settings and adds campaigns to rules

Parameters:

  • settings (Hash)

    The settings configuration

  • vwo_client_instance (VWOClient)

    The VWOClient instance



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/wingify/utils/settings_util.rb', line 24

def set_settings_and_add_campaigns_to_rules(settings, vwo_client_instance)
  # Create settings model and assign it to vwo_client_instance
  vwo_client_instance.settings = SettingsModel.new(settings)
  vwo_client_instance.original_settings = settings

  # Optimize loop by avoiding multiple calls to get_campaigns()
  campaigns = vwo_client_instance.settings.get_campaigns
  campaigns.each_with_index do |campaign, index|
    CampaignUtil.set_variation_allocation(campaign)
    campaigns[index] = campaign
  end

  add_linked_campaigns_to_settings(vwo_client_instance.settings)
  add_is_gateway_service_required_flag(vwo_client_instance.settings)
end