Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions lib/optimizely/decision_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
require_relative './bucketer'

module Optimizely
RESERVED_ATTRIBUTE_KEY_BUCKETING_ID = "\$opt_bucketing_id"

class DecisionService
# Optimizely's decision service that determines into which variation of an experiment a user will be allocated.
#
Expand Down Expand Up @@ -368,9 +366,9 @@ def get_bucketing_id(user_id, attributes)
bucketing_id = user_id

# If the bucketing ID key is defined in attributes, then use that in place of the userID
if attributes && attributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID].is_a?(String)
unless attributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID].empty?
bucketing_id = attributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID]
if attributes && attributes[Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BUCKETING_ID']].is_a?(String)
unless attributes[Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BUCKETING_ID']].empty?
bucketing_id = attributes[Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BUCKETING_ID']]
@config.logger.log(Logger::DEBUG, "Setting the bucketing ID '#{bucketing_id}'")
end
end
Expand Down
54 changes: 27 additions & 27 deletions lib/optimizely/event_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
require 'securerandom'

module Optimizely
RESERVED_ATTRIBUTE_KEY_BUCKETING_ID_EVENT_PARAM_KEY = 'optimizely_bucketing_id'

class Event
# Representation of an event which can be sent to the Optimizely logging endpoint.

Expand Down Expand Up @@ -58,6 +56,13 @@ def initialize(config, logger)

private

def bot_filtering
# Get bot filtering bool
#
# Returns 'botFiltering' value in the datafile.
@config.bot_filtering
end

def get_common_params(user_id, attributes)
# Get params which are used in both conversion and impression events.
#
Expand All @@ -69,33 +74,28 @@ def get_common_params(user_id, attributes)
visitor_attributes = []

attributes&.keys&.each do |attribute_key|
# Omit null attribute value
# Omit null attribute values
attribute_value = attributes[attribute_key]
next if attribute_value.nil?

if attribute_key.eql? RESERVED_ATTRIBUTE_KEY_BUCKETING_ID
# TODO: (Copied from PHP-SDK) (Alda): the type for bucketing ID attribute may change so
# that custom attributes are not overloaded
feature = {
entity_id: RESERVED_ATTRIBUTE_KEY_BUCKETING_ID,
key: RESERVED_ATTRIBUTE_KEY_BUCKETING_ID_EVENT_PARAM_KEY,
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
value: attribute_value
}
else
# Skip attributes not in the datafile
attribute_id = @config.get_attribute_id(attribute_key)
next unless attribute_id

feature = {
entity_id: attribute_id,
key: attribute_key,
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
value: attribute_value
}

unless attribute_value.nil?
attribute_id = @config.get_attribute_id attribute_key
if attribute_id
visitor_attributes.push(
entity_id: attribute_id,
key: attribute_key,
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
value: attribute_value
)
end
end
visitor_attributes.push(feature)
end
# Append Bot Filtering Attribute
if bot_filtering == true || bot_filtering == false
visitor_attributes.push(
entity_id: Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BOT_FILTERING'],
key: Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BOT_FILTERING'],
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
value: bot_filtering
)
end

common_params = {
Expand Down
6 changes: 6 additions & 0 deletions lib/optimizely/helpers/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ module Constants
'VARIABLE_KEY' => 'Variable key',
'VARIABLE_TYPE' => 'Variable type'
}.freeze

CONTROL_ATTRIBUTES = {
'BOT_FILTERING' => '$opt_bot_filtering',
'BUCKETING_ID' => '$opt_bucketing_id',
'USER_AGENT' => '$opt_user_agent'
}.freeze
end
end
end
21 changes: 20 additions & 1 deletion lib/optimizely/project_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.
#
require 'json'
require_relative 'helpers/constants'
require_relative 'helpers/validator'

module Optimizely
Expand All @@ -25,6 +26,7 @@ module Optimizely
class ProjectConfig
# Representation of the Optimizely project config.
RUNNING_EXPERIMENT_STATUS = ['Running'].freeze
RESERVED_ATTRIBUTE_PREFIX = '$opt_'

# Gets project config attributes.
attr_reader :error_handler
Expand All @@ -41,6 +43,7 @@ class ProjectConfig
attr_reader :project_id
# Boolean - denotes if Optimizely should remove the last block of visitors' IP address before storing event data
attr_reader :anonymize_ip
attr_reader :bot_filtering
attr_reader :revision
attr_reader :rollouts
attr_reader :version
Expand Down Expand Up @@ -88,6 +91,7 @@ def initialize(datafile, logger, error_handler)
@groups = config.fetch('groups', [])
@project_id = config['projectId']
@anonymize_ip = config.key?('anonymizeIP') ? config['anonymizeIP'] : false
@bot_filtering = config['botFiltering']
@revision = config['revision']
@rollouts = config.fetch('rollouts', [])

Expand Down Expand Up @@ -363,8 +367,23 @@ def set_forced_variation(experiment_key, user_id, variation_key)
end

def get_attribute_id(attribute_key)
# Get attribute ID for the provided attribute key.
#
# Args:
# Attribute key for which attribute is to be fetched.
#
# Returns:
# Attribute ID corresponding to the provided attribute key.
attribute = @attribute_key_map[attribute_key]
return attribute['id'] if attribute
has_reserved_prefix = attribute_key.to_s.start_with?(RESERVED_ATTRIBUTE_PREFIX)
unless attribute.nil?
if has_reserved_prefix
@logger.log(Logger::WARN, "Attribute '#{attribute_key}' unexpectedly has reserved prefix '#{RESERVED_ATTRIBUTE_PREFIX}'; "\
'using attribute ID instead of reserved attribute name.')
end
return attribute['id']
end
return attribute_key if has_reserved_prefix
@logger.log Logger::ERROR, "Attribute key '#{attribute_key}' is not in datafile."
@error_handler.handle_error InvalidAttributeError
nil
Expand Down
6 changes: 3 additions & 3 deletions spec/decision_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
it 'should return the correct variation ID (using Bucketing ID attrbiute) for a given user for whom a variation has been forced' do
user_attributes = {
'browser_type' => 'firefox',
OptimizelySpec::RESERVED_ATTRIBUTE_KEY_BUCKETING_ID => 'pid'
Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BUCKETING_ID'] => 'pid'
}
config.set_forced_variation('test_experiment_with_audience', 'test_user', 'control_with_audience')
expect(decision_service.get_variation('test_experiment_with_audience', 'test_user', user_attributes)).to eq('122228')
Expand Down Expand Up @@ -88,7 +88,7 @@
it 'should return correct variation ID (using Bucketing ID attrbiute) if user ID is in whitelisted Variations and variation is valid' do
user_attributes = {
'browser_type' => 'firefox',
OptimizelySpec::RESERVED_ATTRIBUTE_KEY_BUCKETING_ID => 'pid'
Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BUCKETING_ID'] => 'pid'
}
expect(decision_service.get_variation('test_experiment', 'forced_user1', user_attributes)).to eq('111128')
expect(spy_logger).to have_received(:log)
Expand Down Expand Up @@ -208,7 +208,7 @@
}
user_attributes = {
'browser_type' => 'firefox',
OptimizelySpec::RESERVED_ATTRIBUTE_KEY_BUCKETING_ID => 'pid'
Optimizely::Helpers::Constants::CONTROL_ATTRIBUTES['BUCKETING_ID'] => 'pid'
}
expect(spy_user_profile_service).to receive(:lookup).once.and_return(nil)

Expand Down
Loading