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
86 changes: 54 additions & 32 deletions lib/splitclient-rb/clients/split_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,64 +35,64 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage
end

def get_treatment(
key, split_name, attributes = {}, split_data = nil, store_impressions = true,
key, split_name, attributes = {}, options = nil, split_data = nil, store_impressions = true,
multiple = false, evaluator = nil
)
result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple)
result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple, options)
return result.tap { |t| t.delete(:config) } if multiple
result[:treatment]
end

def get_treatment_with_config(
key, split_name, attributes = {}, split_data = nil, store_impressions = true,
key, split_name, attributes = {}, options = nil, split_data = nil, store_impressions = true,
multiple = false, evaluator = nil
)
treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple)
treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple, options)
end

def get_treatments(key, split_names, attributes = {})
treatments = treatments(key, split_names, attributes)
def get_treatments(key, split_names, attributes = {}, options = nil)
treatments = treatments(key, split_names, attributes, options)

return treatments if treatments.nil?
keys = treatments.keys
treats = treatments.map { |_,t| t[:treatment] }
Hash[keys.zip(treats)]
end

def get_treatments_with_config(key, split_names, attributes = {})
treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG)
def get_treatments_with_config(key, split_names, attributes = {}, options = nil)
treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG)
end

def get_treatments_by_flag_set(key, flag_set, attributes = {})
def get_treatments_by_flag_set(key, flag_set, attributes = {}, options = nil)
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SET, [flag_set])
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
treatments = treatments(key, split_names, attributes, GET_TREATMENTS_BY_FLAG_SET)
treatments = treatments(key, split_names, attributes, options, GET_TREATMENTS_BY_FLAG_SET)
return treatments if treatments.nil?
keys = treatments.keys
treats = treatments.map { |_,t| t[:treatment] }
Hash[keys.zip(treats)]
end

def get_treatments_by_flag_sets(key, flag_sets, attributes = {})
def get_treatments_by_flag_sets(key, flag_sets, attributes = {}, options = nil)
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SETS, flag_sets)
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
treatments = treatments(key, split_names, attributes, GET_TREATMENTS_BY_FLAG_SETS)
treatments = treatments(key, split_names, attributes, options, GET_TREATMENTS_BY_FLAG_SETS)
return treatments if treatments.nil?
keys = treatments.keys
treats = treatments.map { |_,t| t[:treatment] }
Hash[keys.zip(treats)]
end

def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {})
def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {}, options = nil)
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, [flag_set])
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET)
treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET)
end

def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {})
def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {}, options = nil)
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, flag_sets)
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS)
treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS)
end

def destroy
Expand Down Expand Up @@ -135,10 +135,7 @@ def track(key, traffic_type_name, event_type, value = nil, properties = nil)
if !properties.nil?
properties, size = validate_properties(properties)
properties_size += size
if (properties_size > EVENTS_SIZE_THRESHOLD)
@config.logger.error("The maximum size allowed for the properties is #{EVENTS_SIZE_THRESHOLD}. Current is #{properties_size}. Event not queued")
return false
end
return false unless check_properties_size(properties_size)
end

if ready? && !@config.localhost_mode && !@splits_repository.traffic_type_exists(traffic_type_name)
Expand All @@ -163,6 +160,14 @@ def block_until_ready(time = nil)

private

def check_properties_size(properties_size, msg = "Event not queued")
if (properties_size > EVENTS_SIZE_THRESHOLD)
@config.logger.error("The maximum size allowed for the properties is #{EVENTS_SIZE_THRESHOLD}. Current is #{properties_size}. #{msg}")
return false
end
return true
end

def keys_from_key(key)
case key
when Hash
Expand Down Expand Up @@ -206,7 +211,7 @@ def sanitize_split_names(calling_method, split_names)
end
end

def validate_properties(properties)
def validate_properties(properties, method = 'Event')
properties_count = 0
size = 0

Expand All @@ -225,11 +230,25 @@ def validate_properties(properties)
end
}

@config.logger.warn('Event has more than 300 properties. Some of them will be trimmed when processed') if properties_count > 300
@config.logger.warn("#{method} has more than 300 properties. Some of them will be trimmed when processed") if properties_count > 300

return fixed_properties, size
end

def validate_options(options)
if !options.is_a?(Hash)
@config.logger.warn("Option #{options} should be a hash type. Setting value to nil")
return nil, 0
end
options = options.transform_keys(&:to_sym)
if !options.key?(:properties)
@config.logger.warn("Option #{options} hash does not contain properties key. Setting value to nil")
return nil, 0
end
options[:properties], size = validate_properties(options[:properties], method = 'Treatment')
return options, size
end

def valid_client
if @destroyed
@config.logger.error('Client has already been destroyed - no calls possible')
Expand All @@ -238,8 +257,7 @@ def valid_client
@config.valid_mode
end

def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_treatments')
attributes = {} if attributes.nil?
def treatments(key, feature_flag_names, attributes = {}, options = nil, calling_method = 'get_treatments')
sanitized_feature_flag_names = sanitize_split_names(calling_method, feature_flag_names)

if sanitized_feature_flag_names.nil?
Expand Down Expand Up @@ -269,7 +287,9 @@ def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_t
to_return = Hash.new
sanitized_feature_flag_names.each {|name|
to_return[name.to_sym] = control_treatment_with_config
impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, control_treatment_with_config.merge({ :label => Engine::Models::Label::NOT_READY }), false, { attributes: attributes, time: nil }), :disabled => false }
impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym,
control_treatment_with_config.merge({ :label => Engine::Models::Label::NOT_READY }), false, { attributes: attributes, time: nil },
options), :disabled => false }
}
@impressions_manager.track(impressions)
return to_return
Expand All @@ -291,7 +311,7 @@ def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_t
invalid_treatments[key] = control_treatment_with_config
next
end
treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method)
treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method, false, options)
treatments[key] =
{
treatment: treatments_labels_change_numbers[:treatment],
Expand All @@ -314,7 +334,7 @@ def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_t
# @param store_impressions [Boolean] impressions aren't stored if this flag is false
# @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = true,
calling_method = 'get_treatment', multiple = false)
calling_method = 'get_treatment', multiple = false, options = nil)
impressions = []
bucketing_key, matching_key = keys_from_key(key)

Expand All @@ -332,13 +352,13 @@ def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_i
end

feature_flag = @splits_repository.get_split(feature_flag_name)
treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple)
treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple, options)

@impressions_manager.track(impressions_decorator) unless impressions_decorator.nil?
treatments
end

def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false)
def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false, options = nil)
impressions_decorator = []
begin
start = Time.now
Expand All @@ -359,18 +379,20 @@ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_
impressions_disabled = false
end

options, size = validate_options(options)
options[:properties] = nil unless options.nil? or check_properties_size((EVENT_AVERAGE_SIZE + size), "Properties are ignored")

record_latency(calling_method, start)
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }), :disabled => impressions_disabled }
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }, options), :disabled => impressions_disabled }
impressions_decorator << impression_decorator unless impression_decorator.nil?
rescue StandardError => e
@config.log_found_exception(__method__.to_s, e)
record_exception(calling_method)
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }), :disabled => false }
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }, options), :disabled => false }
impressions_decorator << impression_decorator unless impression_decorator.nil?

return parsed_treatment(control_treatment.merge({ :label => Engine::Models::Label::EXCEPTION }), multiple), impressions_decorator
end

return parsed_treatment(treatment_data, multiple), impressions_decorator
end

Expand Down
3 changes: 2 additions & 1 deletion lib/splitclient-rb/engine/common/impressions_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ def initialize(config,
end

def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {},
properties = nil)
options = nil)
properties = options.nil? ? nil : options[:properties]
impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time], properties)
begin
if @config.impressions_mode == :none || impressions_disabled
Expand Down
2 changes: 1 addition & 1 deletion spec/engine/common/impression_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
'split_name_test',
treatment,
false,
params, {"prop":"val"})
params, {"properties": {"prop":"val"}})
expect(impression).to match(expected)

result_count = impression_counter.pop_all
Expand Down
22 changes: 22 additions & 0 deletions spec/splitclient/split_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@
expect(split_client.get_treatments_by_flag_sets('key', ['set_2'])).to eq({:testing222 => 'off'})
expect(split_client.get_treatments_with_config_by_flag_set('key', 'set_1')).to eq({:testing222 => {:treatment => 'off', :config => nil}})
expect(split_client.get_treatments_with_config_by_flag_sets('key', ['set_2'])).to eq({:testing222 => {:treatment => 'off', :config => nil}})
imps = impressions_repository.batch

expect(split_client.get_treatment('key', 'testing222', {}, {:properties => {:prop => "value"}})).to eq('off')
check_properties(impressions_repository.batch)
expect(split_client.get_treatments('key_prop', ['testing222'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'})
check_properties(impressions_repository.batch)
expect(split_client.get_treatment_with_config('key', 'testing222', {}, {:properties => {:prop => "value"}})).to eq({:treatment => 'off', :config => nil})
check_properties(impressions_repository.batch)
expect(split_client.get_treatments_with_config('key', ['testing222'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}})
check_properties(impressions_repository.batch)
expect(split_client.get_treatments_by_flag_set('key', 'set_1', {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'})
check_properties(impressions_repository.batch)
expect(split_client.get_treatments_by_flag_sets('key', ['set_2'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'})
check_properties(impressions_repository.batch)
expect(split_client.get_treatments_with_config_by_flag_set('key', 'set_1', {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}})
check_properties(impressions_repository.batch)
expect(split_client.get_treatments_with_config_by_flag_sets('key', ['set_2'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}})
check_properties(impressions_repository.batch)
end

it 'check track' do
Expand Down Expand Up @@ -221,3 +239,7 @@ def mock_segment_changes(segment_name, segment_json, since)
stub_request(:get, "https://sdk.split.io/api/segmentChanges/#{segment_name}?since=#{since}")
.to_return(status: 200, body: segment_json)
end

def check_properties(imps)
expect(imps[0][:i][:properties]).to eq({:prop => "value"})
end