Skip to content

Commit

Permalink
feat(eventProperties): Support for event_properties for Data360 enabl…
Browse files Browse the repository at this point in the history
…ed accounts and Bot Elimination
  • Loading branch information
Saksham Gupta authored and rohitesh-wingify committed Apr 8, 2024
1 parent ca46343 commit 4ee8a8c
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 51 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.40.0] - 2024-04-08

### Added

- Support for user IP Address and browser user agent to help with bot elimination, IP specific opt out and more options for post segmentation
- Support for `event_properties` for Data360 enabled accounts

## [1.38.0] - 2022-27-07

### Changed
Expand Down
68 changes: 59 additions & 9 deletions lib/vwo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,14 @@ def activate(campaign_key, user_id, options = {})
@settings_file,
campaign['id'],
variation['id'],
user_id
user_id, nil, nil, nil,
options
)
@batch_events_queue.enqueue(impression)
elsif event_arch_enabled?
properties = get_events_base_properties(@settings_file, EventEnum::VWO_VARIATION_SHOWN, @usage_stats.usage_stats)
payload = get_track_user_payload_data(@settings_file, user_id, EventEnum::VWO_VARIATION_SHOWN, campaign['id'], variation['id'])
@event_dispatcher.dispatch_event_arch_post(properties, payload)
@event_dispatcher.dispatch_event_arch_post(properties, payload, options)
else
# Variation found, dispatch it to server
impression = create_impression(
Expand Down Expand Up @@ -565,6 +566,7 @@ def track(campaign_key, user_id, goal_identifier, options = {})
revenue_value = options[:revenue_value]
custom_variables = options[:custom_variables]
variation_targeting_variables = options[:variation_targeting_variables]
event_properties = options[:event_properties]
goal_type_to_track = get_goal_type_to_track(options)

# Check for valid args
Expand All @@ -591,7 +593,7 @@ def track(campaign_key, user_id, goal_identifier, options = {})

if variation
goal = get_campaign_goal(campaign, goal_identifier)
next unless valid_goal?(goal, campaign, user_id, goal_identifier, revenue_value)
next unless valid_goal?(goal, campaign, user_id, goal_identifier, revenue_value, event_arch_enabled?)

revenue_value = nil if goal['type'] == GoalTypes::CUSTOM
identifiers = get_variation_identifiers(variation)
Expand All @@ -601,8 +603,55 @@ def track(campaign_key, user_id, goal_identifier, options = {})
@variation_decider.update_goal_identifier(user_id, campaign, variation, goal_identifier)
# set variation at user storage

if goal['type'] == GoalTypes::REVENUE && revenue_value.nil?
# mca implementation
if event_arch_enabled?
if goal['mca'] != -1
# Check if eventProperties contain revenueProp for mca != -1
if event_properties.nil?
# Log error if revenueProp is not found in eventProperties
@logger.log(
LogLevelEnum::ERROR,
'Revenue property not found in event properties for revenue goal',
{
'{file}' => FILE,
'{api}' => ApiMethods::TRACK,
'{userId}' => user_id,
'{goalIdentifier}' => goal_identifier,
'{campaignKey}' => campaign['key']
}
)
result[campaign['key']] = false
next
end
elsif goal['type'] == GoalTypes::REVENUE && goal['mca'] == -1
# Check if revenueProp is defined but not found in eventProperties
if goal['revenueProp'] && event_properties.nil?
# Log error if revenueProp is defined but not found in eventProperties
@logger.log(
LogLevelEnum::ERROR,
'Revenue property defined but not found in event properties for revenue goal',
{
'{file}' => FILE,
'{api}' => ApiMethods::TRACK,
'{userId}' => user_id,
'{goalIdentifier}' => goal_identifier,
'{campaignKey}' => campaign['key']
}
)
result[campaign['key']] = false
next
end
end
end
end
if defined?(@batch_events)
impression = create_bulk_event_impression(@settings_file, campaign['id'], variation['id'], user_id, goal['id'], revenue_value)
if event_arch_enabled?
if goal['type'] == GoalTypes::REVENUE && goal['revenueProp'] && event_properties && event_properties.key?(goal['revenueProp'])
revenue_value = event_properties[goal['revenueProp']]
end
end
impression = create_bulk_event_impression(@settings_file, campaign['id'], variation['id'], user_id, goal['id'], revenue_value, event_properties, options)
@batch_events_queue.enqueue(impression)
elsif event_arch_enabled?
metric_map[campaign['id']] = goal['id']
Expand All @@ -612,7 +661,7 @@ def track(campaign_key, user_id, goal_identifier, options = {})
main_keys = { 'campaignId' => campaign['id'], 'variationId' => variation['id'], 'goalId' => goal['id'] }
@event_dispatcher.dispatch(impression, main_keys, EVENTS::TRACK_GOAL)
else
batch_event_data['ev'] << create_bulk_event_impression(@settings_file, campaign['id'], variation['id'], user_id, goal['id'], revenue_value)
batch_event_data['ev'] << create_bulk_event_impression(@settings_file, campaign['id'], variation['id'], user_id, goal['id'], revenue_value, nil, options)
end
result[campaign['key']] = true
next
Expand All @@ -631,8 +680,8 @@ def track(campaign_key, user_id, goal_identifier, options = {})

if event_arch_enabled?
properties = get_events_base_properties(@settings_file, goal_identifier)
payload = get_track_goal_payload_data(@settings_file, user_id, goal_identifier, revenue_value, metric_map, revenue_props)
@event_dispatcher.dispatch_event_arch_post(properties, payload)
payload = get_track_goal_payload_data(@settings_file, user_id, goal_identifier, revenue_value, metric_map, revenue_props, event_properties)
@event_dispatcher.dispatch_event_arch_post(properties, payload, options)
elsif batch_event_data['ev'].count != 0
paramters = get_batch_event_query_params(@settings_file['accountId'], @sdk_key, @usage_stats.usage_stats)
batch_events_dispatcher = VWO::Services::BatchEventsDispatcher.new(@is_development_mode)
Expand Down Expand Up @@ -751,13 +800,14 @@ def feature_enabled?(campaign_key, user_id, options = {})
@settings_file,
campaign['id'],
variation['id'],
user_id
user_id, nil, nil, nil,
options
)
@batch_events_queue.enqueue(impression)
elsif event_arch_enabled?
properties = get_events_base_properties(@settings_file, EventEnum::VWO_VARIATION_SHOWN, @usage_stats.usage_stats)
payload = get_track_user_payload_data(@settings_file, user_id, EventEnum::VWO_VARIATION_SHOWN, campaign['id'], variation['id'])
@event_dispatcher.dispatch_event_arch_post(properties, payload)
@event_dispatcher.dispatch_event_arch_post(properties, payload, options)
else
impression = create_impression(
@settings_file,
Expand Down
2 changes: 1 addition & 1 deletion lib/vwo/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module CONSTANTS
HTTP_PROTOCOL = 'http://'
HTTPS_PROTOCOL = 'https://'
URL_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'
SDK_VERSION = '1.38.0'
SDK_VERSION = '1.40.0'
SDK_NAME = 'ruby'
VWO_DELIMITER = '_vwo_'
MAX_EVENTS_PER_REQUEST = 5000
Expand Down
4 changes: 2 additions & 2 deletions lib/vwo/services/event_dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ def dispatch(impression, main_keys, end_point)
false
end

def dispatch_event_arch_post(params, post_data)
def dispatch_event_arch_post(params, post_data, options = {})
return true if @is_development_mode

url = HTTPS_PROTOCOL + get_url(ENDPOINTS::EVENTS)
resp = VWO::Utils::Request.event_post(url, params, post_data, SDK_NAME)
resp = VWO::Utils::Request.event_post(url, params, post_data, SDK_NAME, options)
if resp.code == '200'
@logger.log(
LogLevelEnum::INFO,
Expand Down
40 changes: 29 additions & 11 deletions lib/vwo/utils/impression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def get_common_properties(user_id, settings_file)
#
# @return[nil|Hash] None if campaign ID or variation ID is invalid,
# Else Properties(dict)
def create_bulk_event_impression(settings_file, campaign_id, variation_id, user_id, goal_id = nil, revenue = nil)
def create_bulk_event_impression(settings_file, campaign_id, variation_id, user_id, goal_id = nil, revenue = nil, event_properties = {} ,options = {})
return unless valid_number?(campaign_id) && valid_string?(user_id)

is_track_user_api = true
Expand All @@ -139,6 +139,15 @@ def create_bulk_event_impression(settings_file, campaign_id, variation_id, user_
sId: get_current_unix_timestamp
}

# Check if user_agent is provided
if options[:user_agent]
impression['visitor_ua'] = options[:user_agent]
end
# Check if user_ip_address is provided
if options[:user_ip_address]
impression['visitor_ip'] = options[:user_ip_address]
end

if is_track_user_api
Logger.log(
LogLevelEnum::DEBUG,
Expand All @@ -151,6 +160,11 @@ def create_bulk_event_impression(settings_file, campaign_id, variation_id, user_
else
impression['g'] = goal_id
impression['r'] = revenue if revenue

if settings_file.key?('isEventArchEnabled') && settings_file['isEventArchEnabled']
impression['eventProps'] = event_properties
end

Logger.log(
LogLevelEnum::DEBUG,
'IMPRESSION_FOR_TRACK_GOAL',
Expand Down Expand Up @@ -198,13 +212,9 @@ def get_event_base_payload(settings_file, user_id, event_name, _usage_stats = {}
sdk_key = settings_file['sdkKey']

props = {
sdkName: SDK_NAME,
sdkVersion: SDK_VERSION,
'$visitor': {
props: {
vwo_fs_environment: sdk_key
}
}
vwo_sdkName: SDK_NAME,
vwo_sdkVersion: SDK_VERSION,

}

# if usage_stats
Expand All @@ -213,7 +223,7 @@ def get_event_base_payload(settings_file, user_id, event_name, _usage_stats = {}

{
d: {
msgId: "#{uuid} + '_' + #{Time.now.to_i}",
msgId: "#{uuid}-#{get_current_unix_timestamp_in_millis}",
visId: uuid,
sessionId: Time.now.to_i,
event: {
Expand Down Expand Up @@ -269,10 +279,11 @@ def get_track_user_payload_data(settings_file, user_id, event_name, campaign_id,
# @param[Integer] :revenue_value
# @param[Hash] :metric_map
# @param[Array] :revenue_props
# @param[Hash] :properties associated with the event.
#
# @return[Hash] :properties
#
def get_track_goal_payload_data(settings_file, user_id, event_name, revenue_value, metric_map, revenue_props = [])
def get_track_goal_payload_data(settings_file, user_id, event_name, revenue_value, metric_map, revenue_props = [], event_properties)
properties = get_event_base_payload(settings_file, user_id, event_name)

metric = {}
Expand Down Expand Up @@ -302,6 +313,13 @@ def get_track_goal_payload_data(settings_file, user_id, event_name, revenue_valu
end

properties[:d][:event][:props][:isCustomEvent] = true

if event_properties && event_properties.any?
event_properties.each do |prop, value|
properties[:d][:event][:props][prop] = value
end
end

properties
end

Expand All @@ -319,7 +337,7 @@ def get_push_payload_data(settings_file, user_id, event_name, custom_dimension_m
properties[:d][:event][:props][:isCustomEvent] = true

custom_dimension_map.each do |tag_key, tag_value|
properties[:d][:event][:props][('$visitor'.to_sym)][:props][tag_key] = tag_value
properties[:d][:event][:props][tag_key] = tag_value
properties[:d][:visitor][:props][tag_key] = tag_value
end

Expand Down
20 changes: 18 additions & 2 deletions lib/vwo/utils/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def self.get(url, params)
Net::HTTP.get_response(uri)
end

def self.post(url, params, post_data)
def self.post(url, params, post_data, options = {})
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
Expand All @@ -35,10 +35,18 @@ def self.post(url, params, post_data)
'Content-Type' => 'application/json',
'Accept' => 'application/json'
}
# Check if user_agent is provided
if options[:user_agent]
headers['X-Device-User-Agent'] = options[:user_agent]
end
# Check if user_ip_address is provided
if options[:user_ip_address]
headers['VWO-X-Forwarded-For'] = options[:user_ip_address]
end
http.post(uri, post_data.to_json, headers)
end

def self.event_post(url, params, post_data, user_agent_value)
def self.event_post(url, params, post_data, user_agent_value, options = {})
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
Expand All @@ -48,6 +56,14 @@ def self.event_post(url, params, post_data, user_agent_value)
'Content-Type' => 'application/json',
'Accept' => 'application/json'
}
# Check if user_agent is provided
if options[:user_agent]
headers['X-Device-User-Agent'] = options[:user_agent]
end
# Check if user_ip_address is provided
if options[:user_ip_address]
headers['VWO-X-Forwarded-For'] = options[:user_ip_address]
end
http.post(uri, post_data.to_json, headers)
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/vwo/utils/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def invalid_config_log(parameter, type, api_name)
)
end

def valid_goal?(goal, campaign, user_id, goal_identifier, revenue_value)
def valid_goal?(goal, campaign, user_id, goal_identifier, revenue_value, is_event_arch_enabled)
if goal.nil? || !goal['id']
Logger.log(
LogLevelEnum::ERROR,
Expand All @@ -166,7 +166,7 @@ def valid_goal?(goal, campaign, user_id, goal_identifier, revenue_value)
}
)
return false
elsif goal['type'] == GoalTypes::REVENUE && !valid_value?(revenue_value)
elsif goal['type'] == GoalTypes::REVENUE && !valid_value?(revenue_value) && !is_event_arch_enabled
Logger.log(
LogLevelEnum::ERROR,
'TRACK_API_REVENUE_NOT_PASSED_FOR_REVENUE_GOAL',
Expand Down
31 changes: 7 additions & 24 deletions tests/test_impression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,11 @@ def test_build_event_arch_payload_for_visitor
sessionId: 123,
event: {
props: {
sdkName: 'string',
sdkVersion: 'string',
vwo_sdkName: 'string',
vwo_sdkVersion: 'string',
id: 12,
isFirst: 1233,
variation: 2,
'$visitor': {
props: {
vwo_fs_environment: 'string'
}
}
},
name: 'string',
time: 12_345
Expand Down Expand Up @@ -78,7 +73,7 @@ def test_build_event_arch_payload_for_goal
}
dummy_revenue_property = %w[dummyRevenueProperty1 dummyRevenueProperty2]
query_params = get_events_base_properties(config, goal_identifier)
properties = get_track_goal_payload_data(config, 'Ashley', goal_identifier, 12, metric_map, dummy_revenue_property)
properties = get_track_goal_payload_data(config, 'Ashley', goal_identifier, 12, metric_map, dummy_revenue_property, {})

expected_properties = {
d: {
Expand All @@ -87,8 +82,8 @@ def test_build_event_arch_payload_for_goal
sessionId: 123,
event: {
props: {
sdkName: 'string',
sdkVersion: 'string',
vwo_sdkName: 'string',
vwo_sdkVersion: 'string',
vwoMeta: {
metric: {
'id_1'.to_sym => ['g_10'],
Expand All @@ -99,11 +94,6 @@ def test_build_event_arch_payload_for_goal
dummyRevenueProperty2: 12
},
isCustomEvent: true,
'$visitor': {
props: {
vwo_fs_environment: 'string'
}
}
},
name: 'string',
time: 12_345
Expand Down Expand Up @@ -136,16 +126,9 @@ def test_build_event_arch_payload_for_push
sessionId: 123,
event: {
props: {
sdkName: 'string',
sdkVersion: 'string',
vwo_sdkName: 'string',
vwo_sdkVersion: 'string',
isCustomEvent: true,
'$visitor': {
props: {
vwo_fs_environment: 'string',
tagKey1: 'tagValue1',
tagKey2: 'tagValue2'
}
}
},
name: 'string',
time: 12_345
Expand Down

0 comments on commit 4ee8a8c

Please sign in to comment.