Skip to content

Commit 1ba8c0b

Browse files
authored
Merge pull request #551 from splitio/rbs-integrations-tests
Integrations tests
2 parents e370d95 + d266ac9 commit 1ba8c0b

File tree

84 files changed

+1525
-363
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+1525
-363
lines changed

lib/splitclient-rb.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
require 'splitclient-rb/engine/metrics/binary_search_latency_tracker'
106106
require 'splitclient-rb/engine/models/split'
107107
require 'splitclient-rb/engine/models/label'
108+
require 'splitclient-rb/engine/models/segment_type'
108109
require 'splitclient-rb/engine/models/treatment'
109110
require 'splitclient-rb/engine/auth_api_client'
110111
require 'splitclient-rb/engine/back_off'

lib/splitclient-rb/cache/fetchers/split_fetcher.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ def call
2525
def fetch_splits(fetch_options = { cache_control_headers: false, till: nil })
2626
@semaphore.synchronize do
2727
data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options)
28-
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config)
28+
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config, @splits_api.clear_storage)
2929
SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config)
3030
@splits_repository.set_segment_names(data[:segment_names])
31+
@rule_based_segments_repository.set_segment_names(data[:segment_names])
3132
@config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled
3233

3334
{ segment_names: data[:segment_names], success: true }

lib/splitclient-rb/cache/repositories/splits_repository.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,7 @@ def initialize(config, flag_sets_repository, flag_set_filter)
4343
end
4444
@flag_sets = flag_sets_repository
4545
@flag_set_filter = flag_set_filter
46-
unless @config.mode.equal?(:consumer)
47-
@adapter.set_string(namespace_key('.splits.till'), '-1')
48-
@adapter.initialize_map(namespace_key('.segments.registered'))
49-
end
46+
initialize_keys
5047
end
5148

5249
def update(to_add, to_delete, new_change_number)
@@ -127,6 +124,7 @@ def clear
127124
@tt_cache.clear
128125

129126
@adapter.clear(namespace_key)
127+
initialize_keys
130128
end
131129

132130
def kill(change_number, split_name, default_treatment)
@@ -167,6 +165,13 @@ def flag_set_filter
167165

168166
private
169167

168+
def initialize_keys
169+
unless @config.mode.equal?(:consumer)
170+
@adapter.set_string(namespace_key('.splits.till'), '-1')
171+
@adapter.initialize_map(namespace_key('.segments.registered'))
172+
end
173+
end
174+
170175
def add_feature_flag(split)
171176
return unless split[:name]
172177
existing_split = get_split(split[:name])

lib/splitclient-rb/clients/split_client.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage
2222
@api_key = sdk_key
2323
@splits_repository = repositories[:splits]
2424
@segments_repository = repositories[:segments]
25+
@rule_based_segments_repository = repositories[:rule_based_segments]
2526
@impressions_repository = repositories[:impressions]
2627
@events_repository = repositories[:events]
2728
@status_manager = status_manager
@@ -115,6 +116,7 @@ def destroy
115116

116117
@splits_repository.clear
117118
@segments_repository.clear
119+
@rule_based_segments_repository.clear
118120

119121
SplitIoClient.load_factory_registry
120122
SplitIoClient.split_factory_registry.remove(@api_key)

lib/splitclient-rb/engine/api/client.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def post_api(url, api_key, data, headers = {}, params = {})
5050
raise e, 'Split SDK failed to connect to backend to post information', e.backtrace
5151
end
5252

53+
def sdk_url_overriden?
54+
@config.sdk_url_overriden?
55+
end
5356
private
5457

5558
def api_client

lib/splitclient-rb/engine/api/splits.rb

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,79 @@ module Api
55
# Retrieves split definitions from the Split Backend
66
class Splits < Client
77

8+
PROXY_CHECK_INTERVAL_SECONDS = 24 * 60 * 60
9+
SPEC_1_1 = "1.1"
10+
811
def initialize(api_key, config, telemetry_runtime_producer)
912
super(config)
1013
@api_key = api_key
1114
@telemetry_runtime_producer = telemetry_runtime_producer
1215
@flag_sets_filter = @config.flag_sets_filter
16+
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
17+
@last_proxy_check_timestamp = 0
18+
@clear_storage = false
19+
@old_spec_since = nil
1320
end
1421

1522
def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil})
1623
start = Time.now
24+
25+
if check_last_proxy_check_timestamp
26+
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
27+
@config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.")
28+
@old_spec_since = since
29+
since = -1
30+
since_rbs = -1
31+
fetch_options = { cache_control_headers: false, till: nil, sets: nil}
32+
end
33+
34+
if @spec_version == Splits::SPEC_1_1
35+
since = @old_spec_since unless @old_spec_since.nil?
36+
params = { s: @spec_version, since: since }
37+
@old_spec_since = nil
38+
else
39+
params = { s: @spec_version, since: since, rbSince: since_rbs }
40+
end
1741

18-
params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs }
1942
params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
2043
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
2144
@config.logger.debug("Fetching from splitChanges with #{params}: ")
2245
response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
46+
2347
if response.status == 414
2448
@config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.")
2549
raise ApiException.new response.body, 414
2650
end
51+
52+
if response.status == 400 and sdk_url_overriden? and @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
53+
@config.logger.warn("Detected proxy response error, changing spec version from #{@spec_version} to #{Splits::SPEC_1_1} and re-fetching.")
54+
@spec_version = Splits::SPEC_1_1
55+
@last_proxy_check_timestamp = Time.now
56+
return since(since, 0, fetch_options = {cache_control_headers: fetch_options[:cache_control_headers], till: fetch_options[:till],
57+
sets: fetch_options[:sets]})
58+
end
59+
2760
if response.success?
28-
result = objects_with_segment_names(response.body)
61+
result = JSON.parse(response.body, symbolize_names: true)
62+
if @spec_version == Splits::SPEC_1_1
63+
result = convert_to_newSPEC(result)
64+
end
65+
66+
result[:rbs][:d] = check_rbs_data(result[:rbs][:d])
67+
result = objects_with_segment_names(result)
2968

69+
if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
70+
@clear_storage = @last_proxy_check_timestamp != 0
71+
@last_proxy_check_timestamp = 0
72+
end
73+
3074
unless result[:ff][:d].empty?
3175
@config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}")
3276
end
3377
@config.split_logger.log_if_transport("Feature flag changes response: #{result[:ff].to_s}")
3478

3579
unless result[:rbs][:d].empty?
36-
@config.split_logger.log_if_debug("#{result[:ff][:d].length} rule based segments retrieved. since=#{since_rbs}")
80+
@config.split_logger.log_if_debug("#{result[:rbs][:d].length} rule based segments retrieved. since=#{since_rbs}")
3781
end
3882
@config.split_logger.log_if_transport("rule based segments changes response: #{result[:rbs].to_s}")
3983

@@ -52,21 +96,42 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till
5296
end
5397
end
5498

99+
def clear_storage
100+
@clear_storage
101+
end
102+
55103
private
56104

57-
def objects_with_segment_names(objects_json)
58-
parsed_objects = JSON.parse(objects_json, symbolize_names: true)
105+
def check_rbs_data(rbs_data)
106+
rbs_data.each do |rb_segment|
107+
rb_segment[:excluded] = {:keys => [], :segments => []} if rb_segment[:excluded].nil?
108+
rb_segment[:excluded][:keys] = [] if rb_segment[:excluded][:keys].nil?
109+
rb_segment[:excluded][:segments] = [] if rb_segment[:excluded][:segments].nil?
110+
end
111+
rbs_data
112+
end
113+
114+
def objects_with_segment_names(parsed_objects)
115+
parsed_objects[:segment_names] = Set.new
59116
parsed_objects[:segment_names] =
60117
parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits|
61118
splits << Helpers::Util.segment_names_by_object(split, "IN_SEGMENT")
62119
end.flatten
63-
if not parsed_objects[:ff][:rbs].nil?
64-
parsed_objects[:segment_names].merge parsed_objects[:ff][:rbs].each_with_object(Set.new) do |rule_based_segment, rule_based_segments|
65-
rule_based_segments << Helpers::Util.segment_names_by_object(rule_based_segment, "IN_SEGMENT")
66-
end.flatten
120+
121+
parsed_objects[:rbs][:d].each do |rule_based_segment|
122+
parsed_objects[:segment_names].merge Helpers::Util.segment_names_in_rb_segment(rule_based_segment, "IN_SEGMENT")
67123
end
124+
68125
parsed_objects
69126
end
127+
128+
def check_last_proxy_check_timestamp
129+
@spec_version == Splits::SPEC_1_1 and ((Time.now - @last_proxy_check_timestamp) >= Splits::PROXY_CHECK_INTERVAL_SECONDS)
130+
end
131+
132+
def convert_to_newSPEC(body)
133+
{:ff => {:d => body[:splits], :s => body[:since], :t => body[:till]}, :rbs => {:d => [], :s => -1, :t => -1}}
134+
end
70135
end
71136
end
72137
end

lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,10 @@ def match?(args)
2525
rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name)
2626
return false if rule_based_segment.nil?
2727

28-
return false if rule_based_segment[:excluded][:keys].include?([args[:value]])
28+
key = update_key(args)
29+
return false if rule_based_segment[:excluded][:keys].include?(key)
2930

30-
rule_based_segment[:excluded][:segments].each do |segment|
31-
return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], args[:value])
32-
33-
return false if segment[:type] == 'rule-based' and SplitIoClient::RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, segment[:name], @config).match?(args)
34-
end
31+
return false unless check_excluded_segments(rule_based_segment, key, args)
3532

3633
matches = false
3734
rule_based_segment[:conditions].each do |c|
@@ -42,5 +39,34 @@ def match?(args)
4239
@logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matches}")
4340
matches
4441
end
42+
43+
private
44+
45+
def check_excluded_segments(rule_based_segment, key, args)
46+
rule_based_segment[:excluded][:segments].each do |segment|
47+
return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD && @segments_repository.in_segment?(segment[:name], key)
48+
49+
return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::RULE_BASED_SEGMENT && match_rbs(
50+
@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args
51+
)
52+
end
53+
true
54+
end
55+
56+
def update_key(args)
57+
if args[:value].nil? || args[:value].empty?
58+
args[:matching_key]
59+
else
60+
args[:value]
61+
end
62+
end
63+
64+
def match_rbs(rule_based_segment, args)
65+
rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository,
66+
rule_based_segment[:name], @config)
67+
rbs_matcher.match?(matching_key: args[:matching_key],
68+
bucketing_key: args[:value],
69+
attributes: args[:attributes])
70+
end
4571
end
4672
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class SplitIoClient::Engine::Models::SegmentType
2+
STANDARD = 'standard'
3+
RULE_BASED_SEGMENT = 'rule-based'
4+
end

lib/splitclient-rb/engine/parser/condition.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def negate
253253
# @return [void]
254254
def set_partitions
255255
partitions_list = []
256-
return partitions_list unless @data.key?('partitions')
256+
return partitions_list unless @data.key?(:partitions) or @data.key?('partitions')
257257

258258
@data[:partitions].each do |p|
259259
partition = SplitIoClient::Partition.new(p)

lib/splitclient-rb/engine/parser/evaluator.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class Evaluator
55
def initialize(segments_repository, splits_repository, rb_segment_repository, config)
66
@splits_repository = splits_repository
77
@segments_repository = segments_repository
8+
@rb_segment_repository = rb_segment_repository
89
@config = config
910
end
1011

@@ -58,7 +59,6 @@ def match(split, keys, attributes)
5859

5960
in_rollout = true
6061
end
61-
6262
condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository, @rb_segment_repository).match?(
6363
matching_key: keys[:matching_key],
6464
bucketing_key: keys[:bucketing_key],

0 commit comments

Comments
 (0)