From 0de56c76f63a56cf50ba6beeda3dc48d5db3ae5f Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 24 Jan 2025 08:21:36 -0800 Subject: [PATCH 01/44] upgrade libs --- spec/integrations/redis_client_spec.rb | 72 +++++++++++++------------- splitclient-rb.gemspec | 3 +- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/spec/integrations/redis_client_spec.rb b/spec/integrations/redis_client_spec.rb index b19733db..af10befa 100644 --- a/spec/integrations/redis_client_spec.rb +++ b/spec/integrations/redis_client_spec.rb @@ -606,17 +606,17 @@ expect(impressions[0][:treatment][:label]).to eq('whitelisted') expect(impressions[0][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[2][:matching_key]).to eq('nico_test') - expect(impressions[2][:split_name]).to eq(:testing) - expect(impressions[2][:treatment][:treatment]).to eq('off') - expect(impressions[2][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[2][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:split_name]).to eq(:testing) expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in segment all') - expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[2][:matching_key]).to eq('nico_test') + expect(impressions[2][:split_name]).to eq(:testing222) + expect(impressions[2][:treatment][:treatment]).to eq('off') + expect(impressions[2][:treatment][:label]).to eq('in segment all') + expect(impressions[2][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do @@ -700,17 +700,17 @@ expect(impressions[2][:treatment][:label]).to eq('whitelisted') expect(impressions[2][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing) - expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[0][:matching_key]).to eq('nico_test') - expect(impressions[0][:split_name]).to eq(:testing222) + expect(impressions[0][:split_name]).to eq(:testing) expect(impressions[0][:treatment][:treatment]).to eq('off') - expect(impressions[0][:treatment][:label]).to eq('in segment all') - expect(impressions[0][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[0][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[0][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[1][:matching_key]).to eq('nico_test') + expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:treatment][:treatment]).to eq('off') + expect(impressions[1][:treatment][:label]).to eq('in segment all') + expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do @@ -792,17 +792,17 @@ expect(impressions[0][:treatment][:label]).to eq('whitelisted') expect(impressions[0][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[2][:matching_key]).to eq('nico_test') - expect(impressions[2][:split_name]).to eq(:testing) - expect(impressions[2][:treatment][:treatment]).to eq('off') - expect(impressions[2][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[2][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:split_name]).to eq(:testing) expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in segment all') - expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[2][:matching_key]).to eq('nico_test') + expect(impressions[2][:split_name]).to eq(:testing222) + expect(impressions[2][:treatment][:treatment]).to eq('off') + expect(impressions[2][:treatment][:label]).to eq('in segment all') + expect(impressions[2][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do @@ -886,17 +886,17 @@ expect(impressions[2][:treatment][:label]).to eq('whitelisted') expect(impressions[2][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing) - expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[0][:matching_key]).to eq('nico_test') - expect(impressions[0][:split_name]).to eq(:testing222) + expect(impressions[0][:split_name]).to eq(:testing) expect(impressions[0][:treatment][:treatment]).to eq('off') - expect(impressions[0][:treatment][:label]).to eq('in segment all') - expect(impressions[0][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[0][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[0][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[1][:matching_key]).to eq('nico_test') + expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:treatment][:treatment]).to eq('off') + expect(impressions[1][:treatment][:label]).to eq('in segment all') + expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do diff --git a/splitclient-rb.gemspec b/splitclient-rb.gemspec index a8adca6a..a2dcf8a7 100644 --- a/splitclient-rb.gemspec +++ b/splitclient-rb.gemspec @@ -48,7 +48,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'simplecov-json', '~> 0.2' spec.add_development_dependency 'timecop', '~> 0.9' spec.add_development_dependency 'webmock', '~> 3.14' - spec.add_development_dependency 'webrick', '~> 1.7' + spec.add_development_dependency 'webrick', '~> 1.8.2' spec.add_runtime_dependency 'bitarray', '~> 1.3' spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0' @@ -61,4 +61,5 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'redis', '>= 4.0.0', '< 6.0' spec.add_runtime_dependency 'socketry', '>= 0.4', '< 1.0' spec.add_runtime_dependency 'thread_safe', '~> 0.3' + spec.add_runtime_dependency 'rexml', '~> 3.3.9' end From f75ab32234aa32c27cafd78ae9c8f654ef828e40 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 24 Jan 2025 08:37:14 -0800 Subject: [PATCH 02/44] revert spec change --- spec/integrations/redis_client_spec.rb | 72 +++++++++++++------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/spec/integrations/redis_client_spec.rb b/spec/integrations/redis_client_spec.rb index af10befa..b19733db 100644 --- a/spec/integrations/redis_client_spec.rb +++ b/spec/integrations/redis_client_spec.rb @@ -606,17 +606,17 @@ expect(impressions[0][:treatment][:label]).to eq('whitelisted') expect(impressions[0][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing) - expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[2][:matching_key]).to eq('nico_test') - expect(impressions[2][:split_name]).to eq(:testing222) + expect(impressions[2][:split_name]).to eq(:testing) expect(impressions[2][:treatment][:treatment]).to eq('off') - expect(impressions[2][:treatment][:label]).to eq('in segment all') - expect(impressions[2][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[2][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[2][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[1][:matching_key]).to eq('nico_test') + expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:treatment][:treatment]).to eq('off') + expect(impressions[1][:treatment][:label]).to eq('in segment all') + expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do @@ -700,17 +700,17 @@ expect(impressions[2][:treatment][:label]).to eq('whitelisted') expect(impressions[2][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[0][:matching_key]).to eq('nico_test') - expect(impressions[0][:split_name]).to eq(:testing) - expect(impressions[0][:treatment][:treatment]).to eq('off') - expect(impressions[0][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[0][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:split_name]).to eq(:testing) expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in segment all') - expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[0][:matching_key]).to eq('nico_test') + expect(impressions[0][:split_name]).to eq(:testing222) + expect(impressions[0][:treatment][:treatment]).to eq('off') + expect(impressions[0][:treatment][:label]).to eq('in segment all') + expect(impressions[0][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do @@ -792,17 +792,17 @@ expect(impressions[0][:treatment][:label]).to eq('whitelisted') expect(impressions[0][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing) - expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[2][:matching_key]).to eq('nico_test') - expect(impressions[2][:split_name]).to eq(:testing222) + expect(impressions[2][:split_name]).to eq(:testing) expect(impressions[2][:treatment][:treatment]).to eq('off') - expect(impressions[2][:treatment][:label]).to eq('in segment all') - expect(impressions[2][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[2][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[2][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[1][:matching_key]).to eq('nico_test') + expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:treatment][:treatment]).to eq('off') + expect(impressions[1][:treatment][:label]).to eq('in segment all') + expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do @@ -886,17 +886,17 @@ expect(impressions[2][:treatment][:label]).to eq('whitelisted') expect(impressions[2][:treatment][:change_number]).to eq(1_506_703_262_916) - expect(impressions[0][:matching_key]).to eq('nico_test') - expect(impressions[0][:split_name]).to eq(:testing) - expect(impressions[0][:treatment][:treatment]).to eq('off') - expect(impressions[0][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') - expect(impressions[0][:treatment][:change_number]).to eq(1_506_440_189_077) - expect(impressions[1][:matching_key]).to eq('nico_test') - expect(impressions[1][:split_name]).to eq(:testing222) + expect(impressions[1][:split_name]).to eq(:testing) expect(impressions[1][:treatment][:treatment]).to eq('off') - expect(impressions[1][:treatment][:label]).to eq('in segment all') - expect(impressions[1][:treatment][:change_number]).to eq(1_505_162_627_437) + expect(impressions[1][:treatment][:label]).to eq('in split test_definition_as_of treatment [off]') + expect(impressions[1][:treatment][:change_number]).to eq(1_506_440_189_077) + + expect(impressions[0][:matching_key]).to eq('nico_test') + expect(impressions[0][:split_name]).to eq(:testing222) + expect(impressions[0][:treatment][:treatment]).to eq('off') + expect(impressions[0][:treatment][:label]).to eq('in segment all') + expect(impressions[0][:treatment][:change_number]).to eq(1_505_162_627_437) end it 'returns treatments with input validation' do From b2fe1619ada6b6f3b857026a0405e3192cfa9785 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 24 Jan 2025 08:46:06 -0800 Subject: [PATCH 03/44] polish --- splitclient-rb.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/splitclient-rb.gemspec b/splitclient-rb.gemspec index a2dcf8a7..3e45076d 100644 --- a/splitclient-rb.gemspec +++ b/splitclient-rb.gemspec @@ -59,7 +59,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'lru_redux', '~> 1.1' spec.add_runtime_dependency 'net-http-persistent', '>= 2.9', '< 5.0' spec.add_runtime_dependency 'redis', '>= 4.0.0', '< 6.0' + spec.add_runtime_dependency 'rexml', '~> 3.3.9' spec.add_runtime_dependency 'socketry', '>= 0.4', '< 1.0' spec.add_runtime_dependency 'thread_safe', '~> 0.3' - spec.add_runtime_dependency 'rexml', '~> 3.3.9' end From fdba26dbd673fabef77c1f0e8c897de9f425da9c Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 24 Jan 2025 10:23:08 -0800 Subject: [PATCH 04/44] polish --- splitclient-rb.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/splitclient-rb.gemspec b/splitclient-rb.gemspec index 3e45076d..3ace0ba4 100644 --- a/splitclient-rb.gemspec +++ b/splitclient-rb.gemspec @@ -42,6 +42,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'pry-nav', '~> 1.0' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rake-compiler', '~> 1.1' + spec.add_development_dependency 'rexml', '~> 3.3.9' spec.add_development_dependency 'rspec', '~> 3.10' spec.add_development_dependency 'rubocop', '1.28.2' spec.add_development_dependency 'simplecov', '~> 0.20' @@ -59,7 +60,6 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'lru_redux', '~> 1.1' spec.add_runtime_dependency 'net-http-persistent', '>= 2.9', '< 5.0' spec.add_runtime_dependency 'redis', '>= 4.0.0', '< 6.0' - spec.add_runtime_dependency 'rexml', '~> 3.3.9' spec.add_runtime_dependency 'socketry', '>= 0.4', '< 1.0' spec.add_runtime_dependency 'thread_safe', '~> 0.3' end From 273f7cd84ead802e6bf5cf0b62a8b9cf6d411e6d Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 24 Jan 2025 10:43:54 -0800 Subject: [PATCH 05/44] removed rexml and upgraded webmock --- splitclient-rb.gemspec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/splitclient-rb.gemspec b/splitclient-rb.gemspec index 3ace0ba4..0a86d725 100644 --- a/splitclient-rb.gemspec +++ b/splitclient-rb.gemspec @@ -42,13 +42,12 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'pry-nav', '~> 1.0' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rake-compiler', '~> 1.1' - spec.add_development_dependency 'rexml', '~> 3.3.9' spec.add_development_dependency 'rspec', '~> 3.10' spec.add_development_dependency 'rubocop', '1.28.2' spec.add_development_dependency 'simplecov', '~> 0.20' spec.add_development_dependency 'simplecov-json', '~> 0.2' spec.add_development_dependency 'timecop', '~> 0.9' - spec.add_development_dependency 'webmock', '~> 3.14' + spec.add_development_dependency 'webmock', '~> 3.24' spec.add_development_dependency 'webrick', '~> 1.8.2' spec.add_runtime_dependency 'bitarray', '~> 1.3' From 5217f632d1054728f26b663af6505300f65bef43 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 24 Jan 2025 11:14:06 -0800 Subject: [PATCH 06/44] fix test --- spec/integrations/dedupe_impression_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integrations/dedupe_impression_spec.rb b/spec/integrations/dedupe_impression_spec.rb index 399d3045..41536789 100644 --- a/spec/integrations/dedupe_impression_spec.rb +++ b/spec/integrations/dedupe_impression_spec.rb @@ -74,7 +74,7 @@ impressions = debug_client.instance_variable_get(:@impressions_repository).batch - sleep 0.5 + sleep 1 expect(impressions.size).to eq 9 end From 79d1565e6bbcf860779e26fadb1e0bcba7d81f12 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Fri, 24 Jan 2025 11:24:27 -0800 Subject: [PATCH 07/44] fix test --- spec/integrations/dedupe_impression_spec.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/integrations/dedupe_impression_spec.rb b/spec/integrations/dedupe_impression_spec.rb index 41536789..c5d0e08c 100644 --- a/spec/integrations/dedupe_impression_spec.rb +++ b/spec/integrations/dedupe_impression_spec.rb @@ -51,11 +51,8 @@ expect(debug_client.get_treatments_with_config_by_flag_sets('admin', ['set_3'])).to eq treatments expect(debug_client.get_treatment('24', 'Test_Save_1')).to eq 'off' expect(debug_client.get_treatment('24', 'Test_Save_1')).to eq 'off' - + sleep 1 impressions = debug_client.instance_variable_get(:@impressions_repository).batch - - sleep 0.5 - expect(impressions.size).to eq 7 end From f856352ff32d8774120348d604f3029a82a77bac Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 22 Apr 2025 10:49:47 -0700 Subject: [PATCH 08/44] Added rbs cache class --- lib/splitclient-rb.rb | 1 + .../rule_based_segments_repository.rb | 123 ++++++++++++++++++ .../rule_based_segments_repository_spec.rb | 109 ++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb create mode 100644 spec/cache/repositories/rule_based_segments_repository_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index 94386cf8..aebc4126 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -23,6 +23,7 @@ require 'splitclient-rb/cache/repositories/splits_repository' require 'splitclient-rb/cache/repositories/events_repository' require 'splitclient-rb/cache/repositories/impressions_repository' +require 'splitclient-rb/cache/repositories/rule_based_segments_repository' require 'splitclient-rb/cache/repositories/events/memory_repository' require 'splitclient-rb/cache/repositories/events/redis_repository' require 'splitclient-rb/cache/repositories/flag_sets/memory_repository' diff --git a/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb new file mode 100644 index 00000000..5ea0e333 --- /dev/null +++ b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb @@ -0,0 +1,123 @@ +require 'concurrent' + +module SplitIoClient + module Cache + module Repositories + class RuleBasedSegmentsRepository < Repository + attr_reader :adapter + DEFAULT_CONDITIONS_TEMPLATE = [{ + conditionType: "ROLLOUT", + matcherGroup: { + combiner: "AND", + matchers: [ + { + keySelector: nil, + matcherType: "ALL_KEYS", + negate: false, + userDefinedSegmentMatcherData: nil, + whitelistMatcherData: nil, + unaryNumericMatcherData: nil, + betweenMatcherData: nil, + dependencyMatcherData: nil, + booleanMatcherData: nil, + stringMatcherData: nil + }] + }, + label: "targeting rule type unsupported by sdk" + }] + + def initialize(config) + super(config) + @tt_cache = {} + @adapter = case @config.cache_adapter.class.to_s + when 'SplitIoClient::Cache::Adapters::RedisAdapter' + SplitIoClient::Cache::Adapters::CacheAdapter.new(@config) + else + @config.cache_adapter + end + unless @config.mode.equal?(:consumer) + @adapter.set_string(namespace_key('.rbsegments.till'), '-1') + @adapter.initialize_map(namespace_key('.segments.registered')) + end + end + + def update(to_add, to_delete, new_change_number) + to_add.each{ |rule_based_segment| add_rule_based_segment(rule_based_segment) } + to_delete.each{ |rule_based_segment| remove_rule_based_segment(rule_based_segment) } + set_change_number(new_change_number) + end + + def get_rule_based_segment(name) + rule_based_segment = @adapter.string(namespace_key(".rbsegment.#{name}")) + + JSON.parse(rule_based_segment, symbolize_names: true) if rule_based_segment + end + + def rule_based_segment_names + @adapter.find_strings_by_prefix(namespace_key('.rbsegment.')) + .map { |rule_based_segment_names| rule_based_segment_names.gsub(namespace_key('.rbsegment.'), '') } + end + + def set_change_number(since) + @adapter.set_string(namespace_key('.rbsegments.till'), since) + end + + def get_change_number + @adapter.string(namespace_key('.rbsegments.till')) + end + + def set_segment_names(names) + return if names.nil? || names.empty? + + names.each do |name| + @adapter.add_to_set(namespace_key('.segments.registered'), name) + end + end + + def exists?(name) + @adapter.exists?(namespace_key(".rbsegment.#{name}")) + end + + def clear + @tt_cache.clear + + @adapter.clear(namespace_key) + end + + def contains?(segment_names) + return set(segment_names).subset?(rule_based_segment_names) + end + + private + + def add_rule_based_segment(rule_based_segment) + return unless rule_based_segment[:name] + existing_rule_based_segment = get_rule_based_segment(rule_based_segment[:name]) + + if check_undefined_matcher(rule_based_segment) + @config.logger.warn("Rule based segment #{rule_based_segment[:name]} has undefined matcher, setting conditions to default template.") + rule_based_segment[:conditions] = RuleBasedSegmentsRepository::DEFAULT_CONDITIONS_TEMPLATE + end + + @adapter.set_string(namespace_key(".rbsegment.#{rule_based_segment[:name]}"), rule_based_segment.to_json) + end + + def check_undefined_matcher(rule_based_segment) + for condition in rule_based_segment[:conditions] + for matcher in condition[:matcherGroup][:matchers] + if !SplitIoClient::Condition.instance_methods(false).map(&:to_s).include?("matcher_#{matcher[:matcherType].downcase}") + @config.logger.error("Detected undefined matcher #{matcher[:matcherType].downcase} in feature flag #{rule_based_segment[:name]}") + return true + end + end + end + return false + end + + def remove_rule_based_segment(rule_based_segment) + @adapter.delete(namespace_key(".rbsegment.#{rule_based_segment[:name]}")) + end + end + end + end +end diff --git a/spec/cache/repositories/rule_based_segments_repository_spec.rb b/spec/cache/repositories/rule_based_segments_repository_spec.rb new file mode 100644 index 00000000..a4e2b9ba --- /dev/null +++ b/spec/cache/repositories/rule_based_segments_repository_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'set' + +describe SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository do + RSpec.shared_examples 'RuleBasedSegments Repository' do |cache_adapter| + let(:config) { SplitIoClient::SplitConfig.new(cache_adapter: cache_adapter) } + let(:repository) { described_class.new(config) } + + before :all do + redis = Redis.new + redis.flushall + end + + before do + # in memory setup + repository.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: []}, + {name: 'bar', trafficTypeName: 'tt_name_2', conditions: []}, + {name: 'baz', trafficTypeName: 'tt_name_1', conditions: []}], [], -1) + end + + after do + repository.update([], [{name: 'foo', trafficTypeName: 'tt_name_1', conditions: []}, + {name: 'bar', trafficTypeName: 'tt_name_2', conditions: []}, + {name: 'bar', trafficTypeName: 'tt_name_2', conditions: []}, + {name: 'qux', trafficTypeName: 'tt_name_3', conditions: []}, + {name: 'quux', trafficTypeName: 'tt_name_4', conditions: []}, + {name: 'corge', trafficTypeName: 'tt_name_5', conditions: []}, + {name: 'corge', trafficTypeName: 'tt_name_6', conditions: []}], -1) + end + + it 'returns rule_based_segments names' do + expect(Set.new(repository.rule_based_segment_names)).to eq(Set.new(%w[foo bar baz])) + end + + it 'returns rule_based_segment data' do + expect(repository.get_rule_based_segment('foo')).to eq( + { conditions: [] , name: 'foo', trafficTypeName: 'tt_name_1' }, + ) + end + + it 'remove undefined matcher with template condition' do + rule_based_segment = { name: 'corge', trafficTypeName: 'tt_name_5', conditions: [ + { + contitionType: 'WHITELIST', + label: 'some_label', + matcherGroup: { + matchers: [ + { + matcherType: 'UNDEFINED', + whitelistMatcherData: { + whitelist: ['k1', 'k2', 'k3'] + }, + negate: false, + } + ], + combiner: 'AND' + } + }] + } + + repository.update([rule_based_segment], [], -1) + expect(repository.get_rule_based_segment('corge')[:conditions]).to eq SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository::DEFAULT_CONDITIONS_TEMPLATE + + # test with multiple conditions + rule_based_segment2 = { + name: 'corge2', + trafficTypeName: 'tt_name_5', + conditions: [ + { + contitionType: 'WHITELIST', + label: 'some_label', + matcherGroup: { + matchers: [ + { + matcherType: 'UNDEFINED', + whitelistMatcherData: { + whitelist: ['k1', 'k2', 'k3'] + }, + negate: false, + } + ], + combiner: 'AND' + } + }, + { + contitionType: 'WHITELIST', + label: 'some_other_label', + matcherGroup: { + matchers: [{matcherType: 'ALL_KEYS', negate: false}], + combiner: 'AND' + } + }] + } + + repository.update([rule_based_segment2], [], -1) + expect(repository.get_rule_based_segment('corge2')[:conditions]).to eq SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository::DEFAULT_CONDITIONS_TEMPLATE + end + end + + describe 'with Memory Adapter' do + it_behaves_like 'RuleBasedSegments Repository', :memory + end + + describe 'with Redis Adapter' do + it_behaves_like 'RuleBasedSegments Repository', :redis + end +end From 4bc3716d50c7203f9d9b9267f884049b9fa7eca8 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 29 Apr 2025 14:18:22 -0700 Subject: [PATCH 09/44] Added RBS matcher --- lib/splitclient-rb.rb | 2 + .../cache/repositories/segments_repository.rb | 7 ++ .../matchers/rule_based_segment_matcher.rb | 45 +++++++++++++ lib/splitclient-rb/engine/parser/condition.rb | 2 + lib/splitclient-rb/engine/parser/evaluator.rb | 31 +-------- .../helpers/evaluator_helper.rb | 36 ++++++++++ .../rule_based_segment_matcher_spec.rb | 66 +++++++++++++++++++ 7 files changed, 159 insertions(+), 30 deletions(-) create mode 100644 lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb create mode 100644 lib/splitclient-rb/helpers/evaluator_helper.rb create mode 100644 spec/engine/matchers/rule_based_segment_matcher_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index aebc4126..b6d04f55 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -48,6 +48,7 @@ require 'splitclient-rb/helpers/decryption_helper' require 'splitclient-rb/helpers/util' require 'splitclient-rb/helpers/repository_helper' +require 'splitclient-rb/helpers/evaluator_helper' require 'splitclient-rb/split_factory' require 'splitclient-rb/split_factory_builder' require 'splitclient-rb/split_config' @@ -97,6 +98,7 @@ require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/between_semver_matcher' require 'splitclient-rb/engine/matchers/in_list_semver_matcher' +require 'splitclient-rb/engine/matchers/rule_based_segment_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/cache/repositories/segments_repository.rb b/lib/splitclient-rb/cache/repositories/segments_repository.rb index 953c18c7..a45ee167 100644 --- a/lib/splitclient-rb/cache/repositories/segments_repository.rb +++ b/lib/splitclient-rb/cache/repositories/segments_repository.rb @@ -83,6 +83,13 @@ def segment_keys_count 0 end + def contains?(segment_names) + if segment_names.empty? + return false + end + return segment_names.to_set.subset?(used_segment_names.to_set) + end + private def segment_data(name) diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb new file mode 100644 index 00000000..fbb5629c --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module SplitIoClient + # + # class to implement the user defined matcher + # + class RuleBasedSegmentMatcher < Matcher + MATCHER_TYPE = 'IN_RULE_BASED_SEGMENT' + + def initialize(rule_based_segments_repository, segments_repository, segment_name, config, evaluator) + super(config.logger) + @rule_based_segments_repository = rule_based_segments_repository + @segments_repository = segments_repository + @segment_name = segment_name + @evaluator = evaluator + @config = config + end + + # + # evaluates if the key matches the matcher + # + # @param key [string] key value to be matched + # + # @return [boolean] evaluation of the key against the segment + def match?(args) + rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name) + return false if rule_based_segment.nil? + + return false if rule_based_segment[:excluded][:keys].include?([args[:value]]) + + return false if @segments_repository.contains?(rule_based_segment[:excluded][:segments]) + + matches = false + rule_based_segment[:conditions].each do |c| + condition = SplitIoClient::Condition.new(c, @config) + next if condition.empty? + + matches = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository).match?(args) + end + @logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matches}") + + matches + end + end +end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index b5d7567b..7fc5582f 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -246,6 +246,8 @@ def negate # @return [void] def set_partitions partitions_list = [] + return partitions_list unless @data.key?('partitions') + @data[:partitions].each do |p| partition = SplitIoClient::Partition.new(p) partitions_list << partition diff --git a/lib/splitclient-rb/engine/parser/evaluator.rb b/lib/splitclient-rb/engine/parser/evaluator.rb index afefd364..b1dfaad0 100644 --- a/lib/splitclient-rb/engine/parser/evaluator.rb +++ b/lib/splitclient-rb/engine/parser/evaluator.rb @@ -59,7 +59,7 @@ def match(split, keys, attributes) in_rollout = true end - condition_matched = matcher_type(condition).match?( + condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository).match?( matching_key: keys[:matching_key], bucketing_key: keys[:bucketing_key], evaluator: self, @@ -80,38 +80,9 @@ def match(split, keys, attributes) treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) end - def matcher_type(condition) - matchers = [] - - @segments_repository.adapter.pipelined do - condition.matchers.each do |matcher| - matchers << if matcher[:negate] - condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher)) - else - matcher_instance(matcher[:matcherType], condition, matcher) - end - end - end - - final_matcher = condition.create_condition_matcher(matchers) - - if final_matcher.nil? - @logger.error('Invalid matcher type') - else - final_matcher - end - end - def treatment_hash(label, treatment, change_number = nil, configurations = nil) { label: label, treatment: treatment, change_number: change_number, config: configurations } end - - def matcher_instance(type, condition, matcher) - condition.send( - "matcher_#{type.downcase}", - matcher: matcher, segments_repository: @segments_repository - ) - end end end end diff --git a/lib/splitclient-rb/helpers/evaluator_helper.rb b/lib/splitclient-rb/helpers/evaluator_helper.rb new file mode 100644 index 00000000..3d6888c3 --- /dev/null +++ b/lib/splitclient-rb/helpers/evaluator_helper.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module SplitIoClient + module Helpers + class EvaluatorHelper + def self.matcher_type(condition, segments_repository) + matchers = [] + + segments_repository.adapter.pipelined do + condition.matchers.each do |matcher| + matchers << if matcher[:negate] + condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher)) + else + matcher_instance(matcher[:matcherType], condition, matcher, segments_repository) + end + end + end + final_matcher = condition.create_condition_matcher(matchers) + + if final_matcher.nil? + config.logger.error('Invalid matcher type') + else + final_matcher + end + final_matcher + end + + def self.matcher_instance(type, condition, matcher, segments_repository) + condition.send( + "matcher_#{type.downcase}", + matcher: matcher, segments_repository: segments_repository + ) + end + end + end +end diff --git a/spec/engine/matchers/rule_based_segment_matcher_spec.rb b/spec/engine/matchers/rule_based_segment_matcher_spec.rb new file mode 100644 index 00000000..d2304ca0 --- /dev/null +++ b/spec/engine/matchers/rule_based_segment_matcher_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::RuleBasedSegmentMatcher do + let(:config) { SplitIoClient::SplitConfig.new(debug_enabled: true) } + let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])} + let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} + let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } + let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, true) } + + + context '#string_type' do + it 'is not string type matcher' do + expect(described_class.new(nil, nil, nil, config, nil).string_type?).to be false + end + end + + context 'test_matcher' do + it 'return false if excluded key is passed' do + rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + rbs_repositoy.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: [], excluded: {keys: ['key1'], segments: []}}], [], -1) + matcher = described_class.new(rbs_repositoy, segments_repository, 'foo', config, nil) + expect(matcher.match?(value: 'key1')).to be false + end + + it 'return false if excluded segment is passed' do + rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + segments_repository.add_to_segment({:name => 'segment1', :added => [], :removed => []}) + rbs_repositoy.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: [], excluded: {keys: ['key1'], segments: ['segment1']}}], [], -1) + matcher = described_class.new(rbs_repositoy, segments_repository, 'foo', config, nil) + expect(matcher.match?(value: 'key2')).to be false + end + + it 'return true if condition matches' do + rule_based_segment = { :name => 'corge', :trafficTypeName => 'tt_name_5', + :excluded => {:keys => [], :segments => []}, + :conditions => [ + { + :contitionType => 'WHITELIST', + :label => 'some_label', + :matcherGroup => { + :matchers => [ + { + :keySelector => nil, + :matcherType => 'WHITELIST', + :whitelistMatcherData => { + :whitelist => ['k1', 'k2', 'k3'] + }, + :negate => false, + } + ], + :combiner => 'AND' + } + }] + } + + rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + rbs_repositoy.update([rule_based_segment], [], -1) + matcher = described_class.new(rbs_repositoy, segments_repository, 'corge', config, evaluator) + expect(matcher.match?({:matching_key => 'user', :attributes => {}})).to be false + expect(matcher.match?({:matching_key => 'k1', :attributes => {}})).to be true + end + end +end \ No newline at end of file From 80c207f165d5317ba9c836f1de1eab8a44739c8c Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 6 May 2025 12:16:14 -0700 Subject: [PATCH 10/44] updated matcher and evaluator --- .../matchers/rule_based_segment_matcher.rb | 29 ++++- lib/splitclient-rb/engine/parser/condition.rb | 7 ++ lib/splitclient-rb/engine/parser/evaluator.rb | 4 +- .../helpers/evaluator_helper.rb | 9 +- .../rule_based_segment_matcher_spec.rb | 111 ++++++++++++++++-- spec/engine/parser/evaluator_spec.rb | 3 +- 6 files changed, 144 insertions(+), 19 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index fbb5629c..7900fe96 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -7,12 +7,11 @@ module SplitIoClient class RuleBasedSegmentMatcher < Matcher MATCHER_TYPE = 'IN_RULE_BASED_SEGMENT' - def initialize(rule_based_segments_repository, segments_repository, segment_name, config, evaluator) + def initialize(segments_repository, rule_based_segments_repository, segment_name, config) super(config.logger) @rule_based_segments_repository = rule_based_segments_repository @segments_repository = segments_repository @segment_name = segment_name - @evaluator = evaluator @config = config end @@ -28,18 +27,40 @@ def match?(args) return false if rule_based_segment[:excluded][:keys].include?([args[:value]]) - return false if @segments_repository.contains?(rule_based_segment[:excluded][:segments]) + rule_based_segment[:excluded][:segments].each do |segment| + return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], args[:value]) + + if segment[:type] == 'rule-based' + return true if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name])[:conditions], args) + end + end matches = false rule_based_segment[:conditions].each do |c| condition = SplitIoClient::Condition.new(c, @config) next if condition.empty? - matches = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository).match?(args) + matches = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository, @rule_based_segments_repository).match?(args) end @logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matches}") matches end + + private + + def match_rbs(conditions, args) + conditions.each do |condition| + next if condition.empty? + + return true if Helpers::EvaluatorHelper::matcher_type(SplitIoClient::Condition.new(condition, @config), + @segments_repository, @rule_based_segments_repository).match?( + matching_key: args[:matching_key], + bucketing_key: args[:value], + attributes: args[:attributes] + ) + end + return false + end end end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index 7fc5582f..926dd206 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -230,6 +230,13 @@ def matcher_in_list_semver(params) ) end + def matcher_in_rule_based_segment(params) + matcher = params[:matcher] + segment_name = matcher[:userDefinedSegmentMatcherData] && matcher[:userDefinedSegmentMatcherData][:segmentName] + + RuleBasedSegmentMatcher.new(params[:segments_repository], params[:rule_based_segments_repository], segment_name, @config) + end + # # @return [object] the negate value for this condition def negate diff --git a/lib/splitclient-rb/engine/parser/evaluator.rb b/lib/splitclient-rb/engine/parser/evaluator.rb index b1dfaad0..b9508e38 100644 --- a/lib/splitclient-rb/engine/parser/evaluator.rb +++ b/lib/splitclient-rb/engine/parser/evaluator.rb @@ -2,7 +2,7 @@ module SplitIoClient module Engine module Parser class Evaluator - def initialize(segments_repository, splits_repository, config) + def initialize(segments_repository, splits_repository, rb_segment_repository, config) @splits_repository = splits_repository @segments_repository = segments_repository @config = config @@ -59,7 +59,7 @@ def match(split, keys, attributes) in_rollout = true end - condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository).match?( + condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository, @rb_segment_repository).match?( matching_key: keys[:matching_key], bucketing_key: keys[:bucketing_key], evaluator: self, diff --git a/lib/splitclient-rb/helpers/evaluator_helper.rb b/lib/splitclient-rb/helpers/evaluator_helper.rb index 3d6888c3..e9c55cfc 100644 --- a/lib/splitclient-rb/helpers/evaluator_helper.rb +++ b/lib/splitclient-rb/helpers/evaluator_helper.rb @@ -3,15 +3,14 @@ module SplitIoClient module Helpers class EvaluatorHelper - def self.matcher_type(condition, segments_repository) + def self.matcher_type(condition, segments_repository, rb_segment_repository) matchers = [] - segments_repository.adapter.pipelined do condition.matchers.each do |matcher| matchers << if matcher[:negate] condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher)) else - matcher_instance(matcher[:matcherType], condition, matcher, segments_repository) + matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository) end end end @@ -25,10 +24,10 @@ def self.matcher_type(condition, segments_repository) final_matcher end - def self.matcher_instance(type, condition, matcher, segments_repository) + def self.matcher_instance(type, condition, matcher, segments_repository, rb_segment_repository) condition.send( "matcher_#{type.downcase}", - matcher: matcher, segments_repository: segments_repository + matcher: matcher, segments_repository: segments_repository, rule_based_segments_repository: rb_segment_repository ) end end diff --git a/spec/engine/matchers/rule_based_segment_matcher_spec.rb b/spec/engine/matchers/rule_based_segment_matcher_spec.rb index d2304ca0..f09b5422 100644 --- a/spec/engine/matchers/rule_based_segment_matcher_spec.rb +++ b/spec/engine/matchers/rule_based_segment_matcher_spec.rb @@ -8,12 +8,10 @@ let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])} let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } - let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, true) } - context '#string_type' do it 'is not string type matcher' do - expect(described_class.new(nil, nil, nil, config, nil).string_type?).to be false + expect(described_class.new(nil, nil, nil, config).string_type?).to be false end end @@ -21,18 +19,51 @@ it 'return false if excluded key is passed' do rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) rbs_repositoy.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: [], excluded: {keys: ['key1'], segments: []}}], [], -1) - matcher = described_class.new(rbs_repositoy, segments_repository, 'foo', config, nil) + matcher = described_class.new(segments_repository, rbs_repositoy, 'foo', config) expect(matcher.match?(value: 'key1')).to be false end it 'return false if excluded segment is passed' do rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rbs_repositoy, true) segments_repository.add_to_segment({:name => 'segment1', :added => [], :removed => []}) - rbs_repositoy.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: [], excluded: {keys: ['key1'], segments: ['segment1']}}], [], -1) - matcher = described_class.new(rbs_repositoy, segments_repository, 'foo', config, nil) + rbs_repositoy.update([{:name => 'foo', :trafficTypeName => 'tt_name_1', :conditions => [], :excluded => {:keys => ['key1'], :segments => [{:name => 'segment1', :type => 'standard'}]}}], [], -1) + matcher = described_class.new(segments_repository, rbs_repositoy, 'foo', config) expect(matcher.match?(value: 'key2')).to be false end + it 'return true if excluded rb segment is matched' do + rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + rbs = {:name => 'sample_rule_based_segment', :trafficTypeName => 'tt_name_1', :conditions => [], :excluded => {:keys => [], :segments => [{:name => 'no_excludes', :type => 'rule-based'}]}} + rbs2 = {:name => 'no_excludes', :trafficTypeName => 'tt_name_1', + :conditions => [{ + :matcherGroup => { + :combiner => "AND", + :matchers => [ + { + :keySelector => { + :trafficType => "user", + :attribute => "email" + }, + :matcherType => "ENDS_WITH", + :negate => false, + :whitelistMatcherData => { + :whitelist => [ + "@split.io" + ] + } + } + ] + } + } + ], :excluded => {:keys => [], :segments => []}} + + rbs_repositoy.update([rbs, rbs2], [], -1) + matcher = described_class.new(segments_repository, rbs_repositoy, 'sample_rule_based_segment', config) + expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be true + expect(matcher.match?(value: 'bilal', attributes: {'email': 'bilal'})).to be false + end + it 'return true if condition matches' do rule_based_segment = { :name => 'corge', :trafficTypeName => 'tt_name_5', :excluded => {:keys => [], :segments => []}, @@ -58,9 +89,75 @@ rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) rbs_repositoy.update([rule_based_segment], [], -1) - matcher = described_class.new(rbs_repositoy, segments_repository, 'corge', config, evaluator) + matcher = described_class.new(segments_repository, rbs_repositoy, 'corge', config) expect(matcher.match?({:matching_key => 'user', :attributes => {}})).to be false expect(matcher.match?({:matching_key => 'k1', :attributes => {}})).to be true end + + it 'return true if dependent rb segment matches' do + rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) + rbs = { + :changeNumber => 5, + :name => "dependent_rbs", + :status => "ACTIVE", + :trafficTypeName => "user", + :excluded =>{:keys =>["mauro@split.io","gaston@split.io"],:segments =>[]}, + :conditions => [ + { + :matcherGroup => { + :combiner => "AND", + :matchers => [ + { + :keySelector => { + :trafficType => "user", + :attribute => "email" + }, + :matcherType => "ENDS_WITH", + :negate => false, + :whitelistMatcherData => { + :whitelist => [ + "@split.io" + ] + } + } + ] + } + } + ]} + rbs2 = { + :changeNumber => 5, + :name => "sample_rule_based_segment", + :status => "ACTIVE", + :trafficTypeName => "user", + :excluded => { + :keys => [], + :segments => [] + }, + :conditions => [ + { + :conditionType => "ROLLOUT", + :matcherGroup => { + :combiner => "AND", + :matchers => [ + { + :keySelector => { + :trafficType => "user" + }, + :matcherType => "IN_RULE_BASED_SEGMENT", + :negate => false, + :userDefinedSegmentMatcherData => { + :segmentName => "dependent_rbs" + } + } + ] + } + } + ] + } + rbs_repositoy.update([rbs, rbs2], [], -1) + matcher = described_class.new(segments_repository, rbs_repositoy, 'sample_rule_based_segment', config) + expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be true + expect(matcher.match?(value: 'bilal', attributes: {'email': 'bilal'})).to be false + end end end \ No newline at end of file diff --git a/spec/engine/parser/evaluator_spec.rb b/spec/engine/parser/evaluator_spec.rb index b3af7d57..99f5cdfb 100644 --- a/spec/engine/parser/evaluator_spec.rb +++ b/spec/engine/parser/evaluator_spec.rb @@ -4,10 +4,11 @@ describe SplitIoClient::Engine::Parser::Evaluator do let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(@default_config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(@default_config) } let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])} let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(@default_config, flag_sets_repository, flag_set_filter) } - let(:evaluator) { described_class.new(segments_repository, splits_repository, true) } + let(:evaluator) { described_class.new(segments_repository, splits_repository, rule_based_segments_repository, true) } let(:killed_split) { { killed: true, defaultTreatment: 'default' } } let(:archived_split) { { status: 'ARCHIVED' } } From ea9928adf0503d57581d1469298d013d49e562e2 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 7 May 2025 18:53:15 -0700 Subject: [PATCH 11/44] Added RBS support for fetcher --- .../cache/fetchers/split_fetcher.rb | 16 +++--- lib/splitclient-rb/engine/api/splits.rb | 37 ++++++++----- .../helpers/repository_helper.rb | 16 ++++++ lib/splitclient-rb/helpers/util.rb | 4 +- lib/splitclient-rb/spec.rb | 2 +- spec/cache/fetchers/segment_fetch_spec.rb | 6 ++- spec/cache/fetchers/split_fetch_spec.rb | 44 ++++++++------- spec/engine/api/splits_spec.rb | 54 +++++++++---------- spec/test_data/splits/splits.json | 41 ++++++++++++-- spec/test_data/splits/splits2.json | 41 ++++++++++++-- spec/test_data/splits/splits3.json | 9 ++-- 11 files changed, 185 insertions(+), 85 deletions(-) diff --git a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb index d39b1aea..204e0e8e 100644 --- a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +++ b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb @@ -2,10 +2,11 @@ module SplitIoClient module Cache module Fetchers class SplitFetcher - attr_reader :splits_repository + attr_reader :splits_repository, :rule_based_segments_repository - def initialize(splits_repository, api_key, config, telemetry_runtime_producer) + def initialize(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer) @splits_repository = splits_repository + @rule_based_segments_repository = rule_based_segments_repository @api_key = api_key @config = config @semaphore = Mutex.new @@ -21,11 +22,12 @@ def call splits_thread end - def fetch_splits(fetch_options = { cache_control_headers: false, till: nil }) + def fetch_splits(fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil }) @semaphore.synchronize do - data = splits_since(@splits_repository.get_change_number, fetch_options) + data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:splits], data[:till], @config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config) + SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config) @splits_repository.set_segment_names(data[:segment_names]) @config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled @@ -55,8 +57,8 @@ def splits_thread end end - def splits_since(since, fetch_options = { cache_control_headers: false, till: nil }) - splits_api.since(since, fetch_options) + def splits_since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil }) + splits_api.since(since, since_rbs, fetch_options) end def splits_api diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index b4d17bda..32d4ad8e 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -12,12 +12,13 @@ def initialize(api_key, config, telemetry_runtime_producer) @flag_sets_filter = @config.flag_sets_filter end - def since(since, fetch_options = { cache_control_headers: false, till: nil, sets: nil}) + def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: nil}) start = Time.now - params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since } + params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs } params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty? params[:till] = fetch_options[:till] unless fetch_options[:till].nil? + params[:till_rbs] = fetch_options[:till_rbs] unless fetch_options[:till_rbs].nil? @config.logger.debug("Fetching from splitChanges with #{params}: ") response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers]) if response.status == 414 @@ -25,11 +26,17 @@ def since(since, fetch_options = { cache_control_headers: false, till: nil, sets raise ApiException.new response.body, 414 end if response.success? - result = splits_with_segment_names(response.body) - unless result[:splits].empty? - @config.split_logger.log_if_debug("#{result[:splits].length} feature flags retrieved. since=#{since}") + result = objects_with_segment_names(response.body) + + unless result[:ff][:d].empty? + @config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}") end - @config.split_logger.log_if_transport("Feature flag changes response: #{result.to_s}") + @config.split_logger.log_if_transport("Feature flag changes response: #{result[:ff].to_s}") + + unless result[:rbs][:d].empty? + @config.split_logger.log_if_debug("#{result[:ff][:d].length} rule based segments retrieved. since=#{since_rbs}") + end + @config.split_logger.log_if_transport("rule based segments changes response: #{result[:rbs].to_s}") bucket = BinarySearchLatencyTracker.get_bucket((Time.now - start) * 1000.0) @telemetry_runtime_producer.record_sync_latency(Telemetry::Domain::Constants::SPLIT_SYNC, bucket) @@ -48,15 +55,19 @@ def since(since, fetch_options = { cache_control_headers: false, till: nil, sets private - def splits_with_segment_names(splits_json) - parsed_splits = JSON.parse(splits_json, symbolize_names: true) + def objects_with_segment_names(objects_json) + parsed_objects = JSON.parse(objects_json, symbolize_names: true) - parsed_splits[:segment_names] = - parsed_splits[:splits].each_with_object(Set.new) do |split, splits| - splits << Helpers::Util.segment_names_by_feature_flag(split) + parsed_objects[:segment_names] = + parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits| + splits << Helpers::Util.segment_names_by_object(split) end.flatten - - parsed_splits + if not parsed_objects[:ff][:rbs].nil? + parsed_objects[:segment_names].merge parsed_objects[:ff][:rbs].each_with_object(Set.new) do |rule_based_segment, rule_based_segments| + rule_based_segments << Helpers::Util.segment_names_by_object(rule_based_segment) + end.flatten + end + parsed_objects end end end diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index 11f42416..24fdd76b 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -25,6 +25,22 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, end feature_flag_repository.update(to_add, to_delete, change_number) end + + def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config) + to_add = [] + to_delete = [] + rule_based_segments.each do |rule_based_segment| + if Engine::Models::Split.archived?(rule_based_segment) + config.logger.debug("removing rule based segment from store(#{rule_based_segment})") if config.debug_enabled + to_delete.push(rule_based_segment) + next + end + + config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled + to_add.push(rule_based_segment) + end + rule_based_segment_repository.update(to_add, to_delete, change_number) + end end end end diff --git a/lib/splitclient-rb/helpers/util.rb b/lib/splitclient-rb/helpers/util.rb index 0cf9aad2..93747d50 100644 --- a/lib/splitclient-rb/helpers/util.rb +++ b/lib/splitclient-rb/helpers/util.rb @@ -3,8 +3,8 @@ module SplitIoClient module Helpers class Util - def self.segment_names_by_feature_flag(feature_flag) - feature_flag[:conditions].each_with_object(Set.new) do |condition, names| + def self.segment_names_by_object(object) + object[:conditions].each_with_object(Set.new) do |condition, names| condition[:matcherGroup][:matchers].each do |matcher| next if matcher[:userDefinedSegmentMatcherData].nil? diff --git a/lib/splitclient-rb/spec.rb b/lib/splitclient-rb/spec.rb index bd991ccc..641e3e63 100644 --- a/lib/splitclient-rb/spec.rb +++ b/lib/splitclient-rb/spec.rb @@ -3,7 +3,7 @@ module SplitIoClient module Spec class FeatureFlags - SPEC_VERSION = "1.1" + SPEC_VERSION = "1.3" end end end diff --git a/spec/cache/fetchers/segment_fetch_spec.rb b/spec/cache/fetchers/segment_fetch_spec.rb index f816f445..031bb2a8 100644 --- a/spec/cache/fetchers/segment_fetch_spec.rb +++ b/spec/cache/fetchers/segment_fetch_spec.rb @@ -36,10 +36,11 @@ let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([]) } let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } let(:segment_fetcher) { described_class.new(segments_repository, '', config, telemetry_runtime_producer) } let(:split_fetcher) do - SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, '', config, telemetry_runtime_producer) + SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, '', config, telemetry_runtime_producer) end it 'fetch segments' do @@ -72,10 +73,11 @@ let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::RedisFlagSetsRepository.new(config) } let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } let(:segment_fetcher) { described_class.new(segments_repository, '', config, telemetry_runtime_producer) } let(:split_fetcher) do - SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, '', config, telemetry_runtime_producer) + SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, '', config, telemetry_runtime_producer) end it 'fetch segments' do diff --git a/spec/cache/fetchers/split_fetch_spec.rb b/spec/cache/fetchers/split_fetch_spec.rb index 957d4101..abe65d01 100644 --- a/spec/cache/fetchers/split_fetch_spec.rb +++ b/spec/cache/fetchers/split_fetch_spec.rb @@ -11,7 +11,7 @@ end before do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: active_splits_json) end @@ -26,20 +26,23 @@ let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([]) } let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:store) { described_class.new(splits_repository, '', config, telemetry_runtime_producer) } + let(:store) { described_class.new(splits_repository, rule_based_segments_repository, '', config, telemetry_runtime_producer) } it 'returns splits since' do - splits = store.send(:splits_since, -1) + splits = store.send(:splits_since, -1, -1) - expect(splits[:splits].count).to eq(2) + expect(splits[:ff][:d].count).to eq(2) + expect(splits[:rbs][:d].count).to eq(1) end it 'fetch data in the cache' do store.send(:fetch_splits) - expect(store.splits_repository.splits.size).to eq(2) - expect(store.splits_repository.get_change_number).to eq(store.send(:splits_since, -1)[:till]) + expect(store.splits_repository.get_change_number).to eq(store.send(:splits_since, -1, -1)[:ff][:t]) + expect(store.rule_based_segments_repository.rule_based_segment_names.size).to eq(1) + expect(store.rule_based_segments_repository.get_change_number).to eq(store.send(:splits_since, -1, -1)[:rbs][:t]) end it 'refreshes splits' do @@ -48,7 +51,7 @@ active_split = store.splits_repository.splits['test_1_ruby'] expect(active_split[:status]).to eq('ACTIVE') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1473413807667') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1473413807667&rbSince=1457726098069') .to_return(status: 200, body: archived_splits_json) store.send(:fetch_splits) @@ -77,25 +80,26 @@ let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new(['set_2']) } let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new(['set_2']) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:store) { described_class.new(splits_repository, '', config, telemetry_runtime_producer) } + let(:store) { described_class.new(splits_repository, rule_based_segments_repository, '', config, telemetry_runtime_producer) } before do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1&sets=set_2') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_2') .to_return(status: 200, body: active_splits_json) end it 'returns splits since' do - splits = store.send(:splits_since, -1) + splits = store.send(:splits_since, -1, -1) - expect(splits[:splits].count).to eq(2) + expect(splits[:ff][:d].count).to eq(2) end it 'fetch data in the cache' do store.send(:fetch_splits) expect(store.splits_repository.splits.size).to eq(1) - expect(store.splits_repository.get_change_number).to eq(store.send(:splits_since, -1)[:till]) + expect(store.splits_repository.get_change_number).to eq(store.send(:splits_since, -1, -1)[:ff][:t]) end it 'refreshes splits' do @@ -103,14 +107,15 @@ expect(store.splits_repository.get_split('sample_feature')[:name]).to eq('sample_feature') expect(store.splits_repository.get_split('test_1_ruby')).to eq(nil) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1473413807667&sets=set_2') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1473413807667&rbSince=1457726098069&sets=set_2') .to_return(status: 200, body: archived_splits_json) store.send(:fetch_splits) expect(store.splits_repository.get_split('sample_feature')).to eq(nil) store.splits_repository.set_change_number(-1) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1&sets=set_2') + store.rule_based_segments_repository.set_change_number(-1) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_2') .to_return(status: 200, body: active_splits_json) store.send(:fetch_splits) @@ -129,18 +134,19 @@ let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::RedisFlagSetsRepository.new(config) } let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:store) { described_class.new(splits_repository, '', config, telemetry_runtime_producer) } + let(:store) { described_class.new(splits_repository, rule_based_segments_repository, '', config, telemetry_runtime_producer) } it 'returns splits since' do - splits = store.send(:splits_since, -1) - expect(splits[:splits].count).to eq(2) + splits = store.send(:splits_since, -1, -1) + expect(splits[:ff][:d].count).to eq(2) end it 'fetch data in the cache' do store.send(:fetch_splits) expect(store.splits_repository.splits.size).to eq(2) - expect(store.splits_repository.get_change_number).to eq(store.send(:splits_since, -1)[:till].to_s) + expect(store.splits_repository.get_change_number).to eq(store.send(:splits_since, -1, -1)[:ff][:t].to_s) end it 'refreshes splits' do @@ -149,7 +155,7 @@ active_split = store.splits_repository.splits['test_1_ruby'] expect(active_split[:status]).to eq('ACTIVE') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1473413807667') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1473413807667&rbSince=1457726098069') .to_return(status: 200, body: archived_splits_json) store.send(:fetch_splits) diff --git a/spec/engine/api/splits_spec.rb b/spec/engine/api/splits_spec.rb index 63e00943..dffb68c5 100644 --- a/spec/engine/api/splits_spec.rb +++ b/spec/engine/api/splits_spec.rb @@ -5,7 +5,7 @@ describe SplitIoClient::Api::Splits do let(:splits) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../../test_data/splits/splits.json'))) } - context '#splits_with_segment_names' do + context '#objects_with_segment_names' do let(:config) do SplitIoClient::SplitConfig.new( logger: Logger.new(log), @@ -18,10 +18,10 @@ let(:splits_api) { described_class.new('', config, telemetry_runtime_producer) } it 'returns splits with segment names' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits) - parsed_splits = splits_api.send(:splits_with_segment_names, splits) + parsed_splits = splits_api.send(:objects_with_segment_names, splits) expect(parsed_splits[:segment_names]).to eq(Set.new(%w[demo employees])) end @@ -41,7 +41,7 @@ let(:splits_api) { described_class.new('', config, telemetry_runtime_producer) } it 'returns the splits - with 2 sets param' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1&sets=set_1,set_2') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2') .with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip', @@ -52,16 +52,16 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: false, till: nil, sets: ['set_1','set_2'] } - returned_splits = splits_api.since(-1, fetch_options) + fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: ['set_1','set_2'] } + returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) expect(log.string).to include '2 feature flags retrieved. since=-1' - expect(log.string).to include returned_splits.to_s + expect(log.string).to include returned_splits["ff"].to_s end it 'raise api exception when status 414' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1&sets=set_1,set_2') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2') .with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip', @@ -72,10 +72,10 @@ }) .to_return(status: 414, body: splits) - fetch_options = { cache_control_headers: false, till: nil, sets: ['set_1','set_2'] } + fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: ['set_1','set_2'] } captured = 0 begin - returned_splits = splits_api.since(-1, fetch_options) + returned_splits = splits_api.since(-1, -1, fetch_options) rescue SplitIoClient::ApiException => e captured = e.exception_code end @@ -96,7 +96,7 @@ let(:splits_api) { described_class.new('', config, telemetry_runtime_producer) } it 'returns the splits - checking headers when cache_control_headers is false' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip', @@ -107,15 +107,15 @@ }) .to_return(status: 200, body: splits) - returned_splits = splits_api.since(-1) + returned_splits = splits_api.since(-1, -1) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) expect(log.string).to include '2 feature flags retrieved. since=-1' - expect(log.string).to include returned_splits.to_s + expect(log.string).to include returned_splits["ff"].to_s end it 'returns the splits - with till param' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1&till=123123') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&&rbSince=-1&till=123123') .with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip', @@ -126,16 +126,16 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: false, till: 123_123, sets: nil } - returned_splits = splits_api.since(-1, fetch_options) + fetch_options = { cache_control_headers: false, till: 123_123, till_rbs: nil, sets: nil } + returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) expect(log.string).to include '2 feature flags retrieved. since=-1' - expect(log.string).to include returned_splits.to_s + expect(log.string).to include returned_splits["ff"].to_s end it 'returns the splits - checking headers when cache_control_headers is true' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip', @@ -147,38 +147,38 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: true, till: nil, sets: nil } - returned_splits = splits_api.since(-1, fetch_options) + fetch_options = { cache_control_headers: true, till: nil, till_rbs: nil, sets: nil } + returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) expect(log.string).to include '2 feature flags retrieved. since=-1' - expect(log.string).to include returned_splits.to_s + expect(log.string).to include returned_splits["ff"].to_s end it 'throws exception if request to get splits from API returns unexpected status code' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 404) - expect { splits_api.since(-1) }.to raise_error( + expect { splits_api.since(-1, -1) }.to raise_error( 'Split SDK failed to connect to backend to fetch feature flags definitions' ) expect(log.string).to include 'Unexpected status code while fetching feature flags' end it 'throws exception if request to get splits from API fails' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_raise(StandardError) - expect { splits_api.since(-1) }.to raise_error( + expect { splits_api.since(-1, -1) }.to raise_error( 'Split SDK failed to connect to backend to retrieve information' ) end it 'throws exception if request to get splits from API times out' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_timeout - expect { splits_api.since(-1) }.to raise_error( + expect { splits_api.since(-1, -1) }.to raise_error( 'Split SDK failed to connect to backend to retrieve information' ) end diff --git a/spec/test_data/splits/splits.json b/spec/test_data/splits/splits.json index 7bd101db..0c08e949 100644 --- a/spec/test_data/splits/splits.json +++ b/spec/test_data/splits/splits.json @@ -1,5 +1,5 @@ -{ - "splits":[ +{"ff": { + "d":[ { "trafficTypeName":"user", "name":"test_1_ruby", @@ -261,6 +261,37 @@ "sets":["set_1","set_2"] } ], - "since":-1, - "till":1473413807667 -} + "s":-1, + "t":1473413807667 + }, "rbs": {"t": 1457726098069, "s": -1, "d": [{ + "changeNumber": 123, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{ + "keys":["mauro@split.io","gaston@split.io"], + "segments":[] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + }]}} \ No newline at end of file diff --git a/spec/test_data/splits/splits2.json b/spec/test_data/splits/splits2.json index d591990a..d688a19d 100644 --- a/spec/test_data/splits/splits2.json +++ b/spec/test_data/splits/splits2.json @@ -1,5 +1,5 @@ -{ - "splits":[ +{"ff":{ + "d":[ { "trafficTypeName":"user", "name":"test_1_ruby", @@ -259,6 +259,37 @@ "sets":["set_1"] } ], - "since":-1, - "till":1473413807667 -} + "s":-1, + "t":1473413807667 + }, "rbs": {"t": 1457726098069, "s": -1, "d": [{ + "changeNumber": 123, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{ + "keys":["mauro@split.io","gaston@split.io"], + "segments":[] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + }]}} \ No newline at end of file diff --git a/spec/test_data/splits/splits3.json b/spec/test_data/splits/splits3.json index c31f3510..baae0c98 100644 --- a/spec/test_data/splits/splits3.json +++ b/spec/test_data/splits/splits3.json @@ -1,5 +1,5 @@ -{ - "splits":[ +{"ff": { + "d":[ { "trafficTypeName":"user", "name":"test_1_ruby", @@ -260,6 +260,7 @@ ] } ], - "since":-1, - "till":1473863097220 + "s":-1, + "t":1473863097220 + }, "rbs": {"d":[], "s":-1, "t":-1} } From 8a9defbdf59210e4de82dc3f18b2ece4b9ffd21a Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 8 May 2025 11:15:44 -0700 Subject: [PATCH 12/44] polish --- lib/splitclient-rb/cache/fetchers/split_fetcher.rb | 4 ++-- lib/splitclient-rb/engine/api/splits.rb | 3 +-- spec/engine/api/splits_spec.rb | 8 ++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb index 204e0e8e..4cf90295 100644 --- a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +++ b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb @@ -22,7 +22,7 @@ def call splits_thread end - def fetch_splits(fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil }) + def fetch_splits(fetch_options = { cache_control_headers: false, till: nil }) @semaphore.synchronize do data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options) @@ -57,7 +57,7 @@ def splits_thread end end - def splits_since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil }) + def splits_since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil }) splits_api.since(since, since_rbs, fetch_options) end diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index 32d4ad8e..1c7a9d0b 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -12,13 +12,12 @@ def initialize(api_key, config, telemetry_runtime_producer) @flag_sets_filter = @config.flag_sets_filter end - def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: nil}) + def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil}) start = Time.now params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs } params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty? params[:till] = fetch_options[:till] unless fetch_options[:till].nil? - params[:till_rbs] = fetch_options[:till_rbs] unless fetch_options[:till_rbs].nil? @config.logger.debug("Fetching from splitChanges with #{params}: ") response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers]) if response.status == 414 diff --git a/spec/engine/api/splits_spec.rb b/spec/engine/api/splits_spec.rb index dffb68c5..af20bd9a 100644 --- a/spec/engine/api/splits_spec.rb +++ b/spec/engine/api/splits_spec.rb @@ -52,7 +52,7 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: ['set_1','set_2'] } + fetch_options = { cache_control_headers: false, till: nil, sets: ['set_1','set_2'] } returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) @@ -72,7 +72,7 @@ }) .to_return(status: 414, body: splits) - fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: ['set_1','set_2'] } + fetch_options = { cache_control_headers: false, till: nil, sets: ['set_1','set_2'] } captured = 0 begin returned_splits = splits_api.since(-1, -1, fetch_options) @@ -126,7 +126,7 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: false, till: 123_123, till_rbs: nil, sets: nil } + fetch_options = { cache_control_headers: false, till: 123_123, sets: nil } returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) @@ -147,7 +147,7 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: true, till: nil, till_rbs: nil, sets: nil } + fetch_options = { cache_control_headers: true, till: nil, sets: nil } returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) From 207532c6fa73e6ecee34860910a55ae36a31ee87 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 8 May 2025 20:39:18 -0700 Subject: [PATCH 13/44] Updated SSE and synchronizer classes --- .../cache/fetchers/split_fetcher.rb | 5 +- .../rule_based_segments_repository.rb | 1 + lib/splitclient-rb/engine/api/splits.rb | 8 +- lib/splitclient-rb/engine/synchronizer.rb | 30 +++- lib/splitclient-rb/helpers/util.rb | 5 +- lib/splitclient-rb/split_factory.rb | 6 +- .../sse/event_source/event_types.rb | 1 + .../sse/notification_processor.rb | 4 +- .../sse/workers/splits_worker.rb | 59 +++++-- spec/engine/api/splits_spec.rb | 8 +- spec/engine/synchronizer_spec.rb | 64 ++++---- spec/sse/event_source/client_spec.rb | 31 ++-- spec/sse/sse_handler_spec.rb | 7 +- spec/sse/workers/segments_worker_spec.rb | 5 +- spec/sse/workers/splits_worker_spec.rb | 146 +++++++++++++----- spec/test_data/integrations/splits.json | 11 +- spec/test_data/integrations/splits_push.json | 11 +- spec/test_data/integrations/splits_push2.json | 9 +- spec/test_data/integrations/splits_push3.json | 11 +- 19 files changed, 276 insertions(+), 146 deletions(-) diff --git a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb index 204e0e8e..8353d51d 100644 --- a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +++ b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb @@ -22,10 +22,9 @@ def call splits_thread end - def fetch_splits(fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil }) + def fetch_splits(fetch_options = { cache_control_headers: false, till: nil }) @semaphore.synchronize do data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config) SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config) @splits_repository.set_segment_names(data[:segment_names]) @@ -57,7 +56,7 @@ def splits_thread end end - def splits_since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil }) + def splits_since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil }) splits_api.since(since, since_rbs, fetch_options) end diff --git a/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb index 5ea0e333..e7e383fb 100644 --- a/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +++ b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb @@ -85,6 +85,7 @@ def clear end def contains?(segment_names) + return false if rule_based_segment_names.empty? return set(segment_names).subset?(rule_based_segment_names) end diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index 32d4ad8e..62e3d4a6 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -12,13 +12,12 @@ def initialize(api_key, config, telemetry_runtime_producer) @flag_sets_filter = @config.flag_sets_filter end - def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: nil}) + def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil}) start = Time.now params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs } params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty? params[:till] = fetch_options[:till] unless fetch_options[:till].nil? - params[:till_rbs] = fetch_options[:till_rbs] unless fetch_options[:till_rbs].nil? @config.logger.debug("Fetching from splitChanges with #{params}: ") response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers]) if response.status == 414 @@ -57,14 +56,13 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till def objects_with_segment_names(objects_json) parsed_objects = JSON.parse(objects_json, symbolize_names: true) - parsed_objects[:segment_names] = parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits| - splits << Helpers::Util.segment_names_by_object(split) + splits << Helpers::Util.segment_names_by_object(split, "IN_SEGMENT") end.flatten if not parsed_objects[:ff][:rbs].nil? parsed_objects[:segment_names].merge parsed_objects[:ff][:rbs].each_with_object(Set.new) do |rule_based_segment, rule_based_segments| - rule_based_segments << Helpers::Util.segment_names_by_object(rule_based_segment) + rule_based_segments << Helpers::Util.segment_names_by_object(rule_based_segment, "IN_SEGMENT") end.flatten end parsed_objects diff --git a/lib/splitclient-rb/engine/synchronizer.rb b/lib/splitclient-rb/engine/synchronizer.rb index d2e0b319..99461670 100644 --- a/lib/splitclient-rb/engine/synchronizer.rb +++ b/lib/splitclient-rb/engine/synchronizer.rb @@ -15,6 +15,7 @@ def initialize( ) @splits_repository = repositories[:splits] @segments_repository = repositories[:segments] + @rule_based_segments_repository = repositories[:rule_based_segments] @impressions_repository = repositories[:impressions] @events_repository = repositories[:events] @config = config @@ -63,12 +64,12 @@ def stop_periodic_fetch @segment_fetcher.stop_segments_thread end - def fetch_splits(target_change_number) - return if target_change_number <= @splits_repository.get_change_number.to_i + def fetch_splits(target_change_number, rbs_target_change_number) + return if check_exit_conditions(target_change_number, rbs_target_change_number) fetch_options = { cache_control_headers: true, till: nil } - result = attempt_splits_sync(target_change_number, + result = attempt_splits_sync(target_change_number, rbs_target_change_number, fetch_options, @config.on_demand_fetch_max_retries, @config.on_demand_fetch_retry_delay_seconds, @@ -82,8 +83,13 @@ def fetch_splits(target_change_number) return end - fetch_options[:till] = target_change_number - result = attempt_splits_sync(target_change_number, + if target_change_number != 0 + fetch_options[:till] = target_change_number + else + fetch_options[:till] = rbs_target_change_number + end + + result = attempt_splits_sync(target_change_number, rbs_target_change_number, fetch_options, ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES, nil, @@ -156,7 +162,7 @@ def attempt_segment_sync(name, target_cn, fetch_options, max_retries, retry_dela end end - def attempt_splits_sync(target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff) + def attempt_splits_sync(target_cn, rbs_target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff) remaining_attempts = max_retries @splits_sync_backoff.reset @@ -165,7 +171,7 @@ def attempt_splits_sync(target_cn, fetch_options, max_retries, retry_delay_secon result = @split_fetcher.fetch_splits(fetch_options) - return sync_result(true, remaining_attempts, result[:segment_names]) if target_cn <= @splits_repository.get_change_number + return sync_result(true, remaining_attempts, result[:segment_names]) if check_exit_conditions(target_cn, rbs_target_cn) return sync_result(false, remaining_attempts, result[:segment_names]) if remaining_attempts <= 0 delay = with_backoff ? @splits_sync_backoff.interval : retry_delay_seconds @@ -206,6 +212,16 @@ def sync_splits_and_segments splits_result[:success] && @segment_fetcher.fetch_segments end + + def check_exit_conditions(target_change_number, rbs_target_change_number) + return true if rbs_target_change_number == 0 and target_change_number == 0 + + return target_change_number <= @splits_repository.get_change_number.to_i if rbs_target_change_number == 0 + + return rbs_target_change_number <= @rule_based_segments_repository.get_change_number.to_i if target_change_number == 0 + + return (target_change_number <= @splits_repository.get_change_number.to_i and rbs_target_change_number <= @rule_based_segments_repository.get_change_number.to_i) + end end end end diff --git a/lib/splitclient-rb/helpers/util.rb b/lib/splitclient-rb/helpers/util.rb index 93747d50..c9b356c4 100644 --- a/lib/splitclient-rb/helpers/util.rb +++ b/lib/splitclient-rb/helpers/util.rb @@ -3,11 +3,10 @@ module SplitIoClient module Helpers class Util - def self.segment_names_by_object(object) + def self.segment_names_by_object(object, matcher_type) object[:conditions].each_with_object(Set.new) do |condition, names| condition[:matcherGroup][:matchers].each do |matcher| - next if matcher[:userDefinedSegmentMatcherData].nil? - + next if matcher[:userDefinedSegmentMatcherData].nil? or matcher[:matcherType] != matcher_type names << matcher[:userDefinedSegmentMatcherData][:segmentName] end end diff --git a/lib/splitclient-rb/split_factory.rb b/lib/splitclient-rb/split_factory.rb index 442298e7..22be6ca7 100644 --- a/lib/splitclient-rb/split_factory.rb +++ b/lib/splitclient-rb/split_factory.rb @@ -154,6 +154,7 @@ def repositories segments: @segments_repository, impressions: @impressions_repository, events: @events_repository, + rule_based_segments: @rule_based_segment_repository } end @@ -178,7 +179,7 @@ def build_telemetry_components end def build_fetchers - @split_fetcher = SplitFetcher.new(@splits_repository, @api_key, @config, @runtime_producer) + @split_fetcher = SplitFetcher.new(@splits_repository, @rule_based_segments_repository, @api_key, @config, @runtime_producer) @segment_fetcher = SegmentFetcher.new(@segments_repository, @api_key, @config, @runtime_producer) end @@ -198,7 +199,7 @@ def build_synchronizer def build_streaming_components @push_status_queue = Queue.new - splits_worker = SSE::Workers::SplitsWorker.new(@synchronizer, @config, @splits_repository, @runtime_producer, @segment_fetcher) + splits_worker = SSE::Workers::SplitsWorker.new(@synchronizer, @config, @splits_repository, @runtime_producer, @segment_fetcher, @rule_based_segment_repository) segments_worker = SSE::Workers::SegmentsWorker.new(@synchronizer, @config, @segments_repository) notification_manager_keeper = SSE::NotificationManagerKeeper.new(@config, @runtime_producer, @push_status_queue) notification_processor = SSE::NotificationProcessor.new(@config, splits_worker, segments_worker) @@ -220,6 +221,7 @@ def build_repositories end @splits_repository = SplitsRepository.new(@config, @flag_sets_repository, @flag_sets_filter) @segments_repository = SegmentsRepository.new(@config) + @rule_based_segment_repository = RuleBasedSegmentRepository.new(@config) @impressions_repository = ImpressionsRepository.new(@config) @events_repository = EventsRepository.new(@config, @api_key, @runtime_producer) end diff --git a/lib/splitclient-rb/sse/event_source/event_types.rb b/lib/splitclient-rb/sse/event_source/event_types.rb index dbe5361d..5396417d 100644 --- a/lib/splitclient-rb/sse/event_source/event_types.rb +++ b/lib/splitclient-rb/sse/event_source/event_types.rb @@ -8,6 +8,7 @@ class EventTypes SPLIT_KILL = 'SPLIT_KILL' SEGMENT_UPDATE = 'SEGMENT_UPDATE' CONTROL = 'CONTROL' + RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE' end end end diff --git a/lib/splitclient-rb/sse/notification_processor.rb b/lib/splitclient-rb/sse/notification_processor.rb index 0c137f59..d723cbdc 100644 --- a/lib/splitclient-rb/sse/notification_processor.rb +++ b/lib/splitclient-rb/sse/notification_processor.rb @@ -13,6 +13,8 @@ def process(incoming_notification) case incoming_notification.data['type'] when SSE::EventSource::EventTypes::SPLIT_UPDATE process_split_update(incoming_notification) + when SSE::EventSource::EventTypes::RB_SEGMENT_UPDATE + process_split_update(incoming_notification) when SSE::EventSource::EventTypes::SPLIT_KILL process_split_kill(incoming_notification) when SSE::EventSource::EventTypes::SEGMENT_UPDATE @@ -25,7 +27,7 @@ def process(incoming_notification) private def process_split_update(notification) - @config.logger.debug("SPLIT UPDATE notification received: #{notification}") if @config.debug_enabled + @config.logger.debug("#{notification.type} notification received: #{notification}") if @config.debug_enabled @splits_worker.add_to_queue(notification) end diff --git a/lib/splitclient-rb/sse/workers/splits_worker.rb b/lib/splitclient-rb/sse/workers/splits_worker.rb index 7971bce8..419a3814 100644 --- a/lib/splitclient-rb/sse/workers/splits_worker.rb +++ b/lib/splitclient-rb/sse/workers/splits_worker.rb @@ -4,7 +4,7 @@ module SplitIoClient module SSE module Workers class SplitsWorker - def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime_producer, segment_fetcher) + def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segment_repository) @synchronizer = synchronizer @config = config @feature_flags_repository = feature_flags_repository @@ -12,6 +12,7 @@ def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime @running = Concurrent::AtomicBoolean.new(false) @telemetry_runtime_producer = telemetry_runtime_producer @segment_fetcher = segment_fetcher + @rule_based_segment_repository = rule_based_segment_repository end def start @@ -54,7 +55,10 @@ def perform case notification.data['type'] when SSE::EventSource::EventTypes::SPLIT_UPDATE success = update_feature_flag(notification) - @synchronizer.fetch_splits(notification.data['changeNumber']) unless success + @synchronizer.fetch_splits(notification.data['changeNumber'], 0) unless success + when SSE::EventSource::EventTypes::RB_SEGMENT_UPDATE + success = update_rule_based_segment(notification) + @synchronizer.fetch_splits(0, notification.data['changeNumber']) unless success when SSE::EventSource::EventTypes::SPLIT_KILL kill_feature_flag(notification) end @@ -64,12 +68,14 @@ def perform def update_feature_flag(notification) return true if @feature_flags_repository.get_change_number.to_i >= notification.data['changeNumber'] return false unless !notification.data['d'].nil? && @feature_flags_repository.get_change_number == notification.data['pcn'] - - new_split = return_split_from_json(notification) + new_split = return_object_from_json(notification) SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository, [new_split], notification.data['changeNumber'], @config) - fetch_segments_if_not_exists(new_split) + fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_SEGMENT"), @feature_flags_repository) + if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_RULE_BASED_SEGMENT"), notification.data['changeNumber']) + return true + end @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS) @@ -80,6 +86,26 @@ def update_feature_flag(notification) false end + def update_rule_based_segment(notification) + return true if @rule_based_segment_repository.get_change_number.to_i >= notification.data['changeNumber'] + return false unless !notification.data['d'].nil? && @rule_based_segment_repository.get_change_number == notification.data['pcn'] + + new_rb_segment = return_object_from_json(notification) + SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository, + [new_rb_segment], + notification.data['changeNumber'], @config) + fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_rb_segment, "IN_SEGMENT"), @rule_based_segment_repository) + +# TODO: enable when telemetry spec is added +# @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS) + + true + rescue StandardError => e + @config.logger.debug("Failed to update Split: #{e.inspect}") if @config.debug_enabled + + false + end + def kill_feature_flag(notification) return if @feature_flags_repository.get_change_number.to_i > notification.data['changeNumber'] @@ -89,21 +115,30 @@ def kill_feature_flag(notification) notification.data['splitName'], notification.data['defaultTreatment'] ) - @synchronizer.fetch_splits(notification.data['changeNumber']) + @synchronizer.fetch_splits(notification.data['changeNumber'], 0) end - def return_split_from_json(notification) - split_json = Helpers::DecryptionHelper.get_encoded_definition(notification.data['c'], notification.data['d']) - JSON.parse(split_json, symbolize_names: true) + def return_object_from_json(notification) + object_json = Helpers::DecryptionHelper.get_encoded_definition(notification.data['c'], notification.data['d']) + JSON.parse(object_json, symbolize_names: true) end - def fetch_segments_if_not_exists(feature_flag) - segment_names = Helpers::Util.segment_names_by_feature_flag(feature_flag) + def fetch_segments_if_not_exists(segment_names, object_repository) + return if segment_names.nil? - @feature_flags_repository.set_segment_names(segment_names) + object_repository.set_segment_names(segment_names) @segment_fetcher.fetch_segments_if_not_exists(segment_names) end + + def fetch_rule_based_segments_if_not_exists(segment_names, change_number) + if segment_names.nil? or segment_names.empty? or @rule_based_segment_repository.contains?(segment_names.to_a) + return false + end + @synchronizer.fetch_splits(0, change_number) + + true + end end end end diff --git a/spec/engine/api/splits_spec.rb b/spec/engine/api/splits_spec.rb index dffb68c5..af20bd9a 100644 --- a/spec/engine/api/splits_spec.rb +++ b/spec/engine/api/splits_spec.rb @@ -52,7 +52,7 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: ['set_1','set_2'] } + fetch_options = { cache_control_headers: false, till: nil, sets: ['set_1','set_2'] } returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) @@ -72,7 +72,7 @@ }) .to_return(status: 414, body: splits) - fetch_options = { cache_control_headers: false, till: nil, till_rbs: nil, sets: ['set_1','set_2'] } + fetch_options = { cache_control_headers: false, till: nil, sets: ['set_1','set_2'] } captured = 0 begin returned_splits = splits_api.since(-1, -1, fetch_options) @@ -126,7 +126,7 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: false, till: 123_123, till_rbs: nil, sets: nil } + fetch_options = { cache_control_headers: false, till: 123_123, sets: nil } returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) @@ -147,7 +147,7 @@ }) .to_return(status: 200, body: splits) - fetch_options = { cache_control_headers: true, till: nil, till_rbs: nil, sets: nil } + fetch_options = { cache_control_headers: true, till: nil, sets: nil } returned_splits = splits_api.since(-1, -1, fetch_options) expect(returned_splits[:segment_names]).to eq(Set.new(%w[demo employees])) diff --git a/spec/engine/synchronizer_spec.rb b/spec/engine/synchronizer_spec.rb index 4a920b55..cef2020d 100644 --- a/spec/engine/synchronizer_spec.rb +++ b/spec/engine/synchronizer_spec.rb @@ -18,16 +18,18 @@ flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) splits_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) segments_repository = SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) + rule_based_segments_repository = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) repositories = { splits: splits_repository, segments: segments_repository, impressions: SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config), - events: SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, runtime_producer) + events: SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, runtime_producer), + rule_based_segments: rule_based_segments_repository } parameters = { - split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, runtime_producer), + split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, runtime_producer), segment_fetcher: SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, runtime_producer), telemetry_runtime_producer: runtime_producer, unique_keys_tracker: SplitIoClient::Engine::Impressions::NoopUniqueKeysTracker.new @@ -59,7 +61,7 @@ sleep(2) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=-1')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=1470947453877')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment2?since=-1')).to have_been_made.once @@ -73,7 +75,7 @@ sleep(2) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=-1')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=1470947453877')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment2?since=-1')).to have_been_made.once @@ -91,7 +93,7 @@ it 'start_periodic_fetch' do synchronizer.start_periodic_fetch - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=-1')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=1470947453877')).to have_been_made.once expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment2?since=-1')).to have_been_made.once @@ -101,7 +103,7 @@ end it 'sync_all synchronous - should return false' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').to_return(status: 500) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').to_return(status: 500) result = synchronizer.sync_all(false) @@ -118,40 +120,40 @@ mock_segment_changes('segment1', segment1, '-1') mock_segment_changes('segment1', segment1, '1470947453877') - synchronizer.fetch_splits(0) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.once + synchronizer.fetch_splits(1506703262916, 0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.once end it 'fetch_splits - with CDN bypassed' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: - '{ - "splits": [], - "since": -1, - "till": 1506703262918 - }') + '{"ff":{ + "d": [], + "s": -1, + "t": 1506703262918 + }, "rbs":{"d":[],"s":-1,"t":-1}}') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1') .to_return(status: 200, body: - '{ - "splits": [], - "since": 1506703262918, - "till": 1506703262918 - }') + '{"ff":{ + "d": [], + "s": 1506703262918, + "t": 1506703262918 + }, "rbs":{"d":[],"s":-1,"t":-1}}') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918&till=1506703262920') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1&till=1506703262920') .to_return(status: 200, body: - '{ - "splits": [], - "since": 1506703262918, - "till": 1506703262921 - }') + '{"ff":{ + "d": [], + "s": 1506703262918, + "t": 1506703262921 + }, "rbs":{"d":[],"s":-1,"t":-1}}') - synchronizer.fetch_splits(1_506_703_262_920) + synchronizer.fetch_splits(1_506_703_262_920, 0) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.once - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918')).to have_been_made.times(9) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918&till=1506703262920')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1')).to have_been_made.times(9) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1&till=1506703262920')).to have_been_made.once end it 'fetch_segment' do @@ -234,7 +236,7 @@ private def mock_split_changes(splits_json) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').to_return(status: 200, body: splits_json) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').to_return(status: 200, body: splits_json) end def mock_segment_changes(segment_name, segment_json, since) diff --git a/spec/sse/event_source/client_spec.rb b/spec/sse/event_source/client_spec.rb index 3547f6a1..1c199b09 100644 --- a/spec/sse/event_source/client_spec.rb +++ b/spec/sse/event_source/client_spec.rb @@ -20,19 +20,20 @@ splits: SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter), segments: SplitIoClient::Cache::Repositories::SegmentsRepository.new(config), impressions: SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config), - events: SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, telemetry_runtime_producer) + events: SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, telemetry_runtime_producer), + rule_based_segments: SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } end let(:parameters) do { - split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(repositories[:splits], api_key, config, telemetry_runtime_producer), + split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(repositories[:splits], repositories[:rule_based_segments], api_key, config, telemetry_runtime_producer), segment_fetcher: SplitIoClient::Cache::Fetchers::SegmentFetcher.new(repositories[:segments], api_key, config, telemetry_runtime_producer), imp_counter: SplitIoClient::Engine::Common::ImpressionCounter.new, telemetry_runtime_producer: telemetry_runtime_producer } end let(:synchronizer) { SplitIoClient::Engine::Synchronizer.new(repositories, config, parameters) } - let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, repositories[:splits], telemetry_runtime_producer, parameters[:segment_fetcher]) } + let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, repositories[:splits], telemetry_runtime_producer, parameters[:segment_fetcher], repositories[:rule_based_segments]) } let(:segments_worker) { SplitIoClient::SSE::Workers::SegmentsWorker.new(synchronizer, config, repositories[:segments]) } let(:push_status_queue) { Queue.new } let(:notification_manager_keeper) { SplitIoClient::SSE::NotificationManagerKeeper.new(config, telemetry_runtime_producer, push_status_queue) } @@ -48,12 +49,12 @@ context 'tests' do it 'receive split update event' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .with(headers: { 'Authorization' => 'Bearer client-spec-key' }) - .to_return(status: 200, body: '{"splits":[],"since":-1,"till":5564531221}') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=5564531221') + .to_return(status: 200, body: '{"ff":{"d":[],"s":-1,"t":5564531221}, "rbs":{"d":[],"s":-1,"t":-1}}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=5564531221&rbSince=-1') .with(headers: { 'Authorization' => 'Bearer client-spec-key' }) - .to_return(status: 200, body: '{"splits":[],"since":5564531221,"till":5564531221}') + .to_return(status: 200, body: '{"ff":{"d":[],"s":5564531221,"t":5564531221}, "rbs":{"d":[],"s":-1,"t":-1}}') mock_server do |server| server.setup_response('/') do |_, res| @@ -68,7 +69,7 @@ expect(sse_client.connected?).to eq(true) expect(push_status_queue.pop(true)).to eq(SplitIoClient::Constants::PUSH_CONNECTED) sleep 1 - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(1) sse_client.close @@ -79,12 +80,12 @@ end it 'receive split kill event' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .with(headers: { 'Authorization' => 'Bearer client-spec-key' }) - .to_return(status: 200, body: '{"splits":[],"since":-1,"till":5564531221}') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=5564531221') + .to_return(status: 200, body: '{"ff":{"d":[],"since":-1,"till":5564531221}, "rbs":{"d":[],"s":-1,"t":-1}}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=5564531221&rbSince=-1') .with(headers: { 'Authorization' => 'Bearer client-spec-key' }) - .to_return(status: 200, body: '{"splits":[],"since":5564531221,"till":5564531221}') + .to_return(status: 200, body: '{"ff":{"d":[],"since":5564531221,"till":5564531221}, "rbs":{"d":[],"s":-1,"t":-1}}') mock_server do |server| server.setup_response('/') do |_, res| @@ -99,7 +100,7 @@ expect(sse_client.connected?).to eq(true) expect(push_status_queue.pop(true)).to eq(SplitIoClient::Constants::PUSH_CONNECTED) sleep 1 - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(1) sse_client.close @@ -150,7 +151,7 @@ expect(connected).to eq(true) expect(sse_client.connected?).to eq(true) expect(push_status_queue.pop(true)).to eq(SplitIoClient::Constants::PUSH_CONNECTED) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(0) expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment-test?since=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(0) sse_client.close @@ -172,7 +173,7 @@ expect(connected).to eq(true) expect(sse_client.connected?).to eq(true) expect(push_status_queue.pop(true)).to eq(SplitIoClient::Constants::PUSH_CONNECTED) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(0) expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment-test?since=-1').with(headers: { 'Authorization' => 'Bearer client-spec-key' })).to have_been_made.times(0) sse_client.close diff --git a/spec/sse/sse_handler_spec.rb b/spec/sse/sse_handler_spec.rb index e6a314dc..099f7932 100644 --- a/spec/sse/sse_handler_spec.rb +++ b/spec/sse/sse_handler_spec.rb @@ -21,19 +21,20 @@ splits: splits_repository, segments: segments_repository, impressions: SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config), - events: SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, telemetry_runtime_producer) + events: SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, telemetry_runtime_producer), + rule_based_segments: SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } end let(:parameters) do { - split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, telemetry_runtime_producer), + split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, repositories[:rule_based_segments], api_key, config, telemetry_runtime_producer), segment_fetcher: SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, telemetry_runtime_producer), imp_counter: SplitIoClient::Engine::Common::ImpressionCounter.new, telemetry_runtime_producer: telemetry_runtime_producer } end let(:synchronizer) { SplitIoClient::Engine::Synchronizer.new(repositories, config, parameters) } - let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, splits_repository, telemetry_runtime_producer, parameters[:segment_fetcher]) } + let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, splits_repository, telemetry_runtime_producer, parameters[:segment_fetcher], repositories[:rule_based_segments]) } let(:segments_worker) { SplitIoClient::SSE::Workers::SegmentsWorker.new(synchronizer, config, segments_repository) } let(:notification_processor) { SplitIoClient::SSE::NotificationProcessor.new(config, splits_worker, segments_worker) } let(:event_parser) { SplitIoClient::SSE::EventSource::EventParser.new(config) } diff --git a/spec/sse/workers/segments_worker_spec.rb b/spec/sse/workers/segments_worker_spec.rb index aad5db85..8e3405bc 100644 --- a/spec/sse/workers/segments_worker_spec.rb +++ b/spec/sse/workers/segments_worker_spec.rb @@ -17,10 +17,11 @@ let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:impressions_repository) { SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } let(:events_repository) { SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, telemetry_runtime_producer) } - let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, telemetry_runtime_producer) } + let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer) } let(:segment_fetcher) { SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, telemetry_runtime_producer) } let(:repositories) { { splits: splits_repository, segments: segments_repository } } let(:impression_counter) { SplitIoClient::Engine::Common::ImpressionCounter.new } @@ -101,7 +102,7 @@ private def mock_split_changes(splits_json) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) end diff --git a/spec/sse/workers/splits_worker_spec.rb b/spec/sse/workers/splits_worker_spec.rb index b45aef73..0ef724f9 100644 --- a/spec/sse/workers/splits_worker_spec.rb +++ b/spec/sse/workers/splits_worker_spec.rb @@ -21,13 +21,16 @@ let(:event_split_archived_no_compression) { SplitIoClient::SSE::EventSource::StreamData.new("data", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":5564531221,"pcn":1234,"c": 0,"d":"eyJ0cmFmZmljVHlwZU5hbWUiOiAidXNlciIsICJpZCI6ICIzM2VhZmE1MC0xYTY1LTExZWQtOTBkZi1mYTMwZDk2OTA0NDUiLCAibmFtZSI6ICJiaWxhbF9zcGxpdCIsICJ0cmFmZmljQWxsb2NhdGlvbiI6IDEwMCwgInRyYWZmaWNBbGxvY2F0aW9uU2VlZCI6IC0xMzY0MTE5MjgyLCAic2VlZCI6IC02MDU5Mzg4NDMsICJzdGF0dXMiOiAiQVJDSElWRUQiLCAia2lsbGVkIjogZmFsc2UsICJkZWZhdWx0VHJlYXRtZW50IjogIm9mZiIsICJjaGFuZ2VOdW1iZXIiOiAxNjg0Mjc1ODM5OTUyLCAiYWxnbyI6IDIsICJjb25maWd1cmF0aW9ucyI6IHt9LCAiY29uZGl0aW9ucyI6IFt7ImNvbmRpdGlvblR5cGUiOiAiUk9MTE9VVCIsICJtYXRjaGVyR3JvdXAiOiB7ImNvbWJpbmVyIjogIkFORCIsICJtYXRjaGVycyI6IFt7ImtleVNlbGVjdG9yIjogeyJ0cmFmZmljVHlwZSI6ICJ1c2VyIn0sICJtYXRjaGVyVHlwZSI6ICJJTl9TRUdNRU5UIiwgIm5lZ2F0ZSI6IGZhbHNlLCAidXNlckRlZmluZWRTZWdtZW50TWF0Y2hlckRhdGEiOiB7InNlZ21lbnROYW1lIjogImJpbGFsX3NlZ21lbnQifX1dfSwgInBhcnRpdGlvbnMiOiBbeyJ0cmVhdG1lbnQiOiAib24iLCAic2l6ZSI6IDB9LCB7InRyZWF0bWVudCI6ICJvZmYiLCAic2l6ZSI6IDEwMH1dLCAibGFiZWwiOiAiaW4gc2VnbWVudCBiaWxhbF9zZWdtZW50In0sIHsiY29uZGl0aW9uVHlwZSI6ICJST0xMT1VUIiwgIm1hdGNoZXJHcm91cCI6IHsiY29tYmluZXIiOiAiQU5EIiwgIm1hdGNoZXJzIjogW3sia2V5U2VsZWN0b3IiOiB7InRyYWZmaWNUeXBlIjogInVzZXIifSwgIm1hdGNoZXJUeXBlIjogIkFMTF9LRVlTIiwgIm5lZ2F0ZSI6IGZhbHNlfV19LCAicGFydGl0aW9ucyI6IFt7InRyZWF0bWVudCI6ICJvbiIsICJzaXplIjogMH0sIHsidHJlYXRtZW50IjogIm9mZiIsICJzaXplIjogMTAwfV0sICJsYWJlbCI6ICJkZWZhdWx0IHJ1bGUifV19"}'), 'test') } let(:event_split_update_no_definition) { SplitIoClient::SSE::EventSource::StreamData.new("data", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":5564531221,"pcn":1234,"c": 0, "d":null}'), 'test') } let(:event_split_update_segments) { SplitIoClient::SSE::EventSource::StreamData.new("data", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":5564531221,"pcn":1234,"c":2,"d":"eJzcVFtr20wQ/SvhPK9AsnzTvpnPJp+po0DlppRgzFga2dusJLNapaRG/73Id7sOoU+FvmluZ3TOGXYDayhNVTx9W3NIGUOiKtlAQCWQSNq+FyeJ6yzcBTuex+T0qe86XrfrUkJBzH4AgXw3mVFlivl3eiWIA/BA6yImq4oc0nPdG/mIOYF0gpYfeO3AEyh3Ca/XDfxer+u2BUpLtiohMfhvOn4aQeBFad20paRLFkg4pUrbqWGyGecWEvbwPQ9cCMQrypccVtmCDaTX7feCnu+7nY7nCZBeFpAtgbjIU7WszPbPSshNvc0lah8/b05hoxkkvv4/no4m42gKgYxsvGJzb4pqDdn0ZguVNwsxCIenhh3SPriBk/OSLB/Z/Vgpy1qV9mE3MSRLDfwxD/kMSjKVb1dUpmgwVFxgVtezWmBNxp5RsDdlavkdCJTqJ2+tqmcCmhasIU+LOEEtftfg8+Nk8vjlzxV44beINce2ME3z2TEeDrEWVzKNw3k0un8YhTd0aiaGnKqck4iXDakrwcpdNjzdq9PChxIV+VEXt2F/UUvTC9Guyk/t90dfO+/Xro73w65z7y6cU/ndnvTdge7f9W8wmcw/jb5F1+79yybsX6c7U2lGPat/BQAA//9ygdKB"}'), 'test') } + let(:event_split_update_rb_segments) { SplitIoClient::SSE::EventSource::StreamData.new("data", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":5564531221,"pcn":1234,"c":0,"d":"eyJjaGFuZ2VOdW1iZXIiOiAxMCwgInRyYWZmaWNUeXBlTmFtZSI6ICJ1c2VyIiwgIm5hbWUiOiAicmJzX2ZsYWciLCAidHJhZmZpY0FsbG9jYXRpb24iOiAxMDAsICJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOiAxODI4Mzc3MzgwLCAic2VlZCI6IC0yODY2MTc5MjEsICJzdGF0dXMiOiAiQUNUSVZFIiwgImtpbGxlZCI6IGZhbHNlLCAiZGVmYXVsdFRyZWF0bWVudCI6ICJvZmYiLCAiYWxnbyI6IDIsICJjb25kaXRpb25zIjogW3siY29uZGl0aW9uVHlwZSI6ICJST0xMT1VUIiwgIm1hdGNoZXJHcm91cCI6IHsiY29tYmluZXIiOiAiQU5EIiwgIm1hdGNoZXJzIjogW3sia2V5U2VsZWN0b3IiOiB7InRyYWZmaWNUeXBlIjogInVzZXIifSwgIm1hdGNoZXJUeXBlIjogIklOX1JVTEVfQkFTRURfU0VHTUVOVCIsICJuZWdhdGUiOiBmYWxzZSwgInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjogeyJzZWdtZW50TmFtZSI6ICJzYW1wbGVfcnVsZV9iYXNlZF9zZWdtZW50In19XX0sICJwYXJ0aXRpb25zIjogW3sidHJlYXRtZW50IjogIm9uIiwgInNpemUiOiAxMDB9LCB7InRyZWF0bWVudCI6ICJvZmYiLCAic2l6ZSI6IDB9XSwgImxhYmVsIjogImluIHJ1bGUgYmFzZWQgc2VnbWVudCBzYW1wbGVfcnVsZV9iYXNlZF9zZWdtZW50In0sIHsiY29uZGl0aW9uVHlwZSI6ICJST0xMT1VUIiwgIm1hdGNoZXJHcm91cCI6IHsiY29tYmluZXIiOiAiQU5EIiwgIm1hdGNoZXJzIjogW3sia2V5U2VsZWN0b3IiOiB7InRyYWZmaWNUeXBlIjogInVzZXIifSwgIm1hdGNoZXJUeXBlIjogIkFMTF9LRVlTIiwgIm5lZ2F0ZSI6IGZhbHNlfV19LCAicGFydGl0aW9ucyI6IFt7InRyZWF0bWVudCI6ICJvbiIsICJzaXplIjogMH0sIHsidHJlYXRtZW50IjogIm9mZiIsICJzaXplIjogMTAwfV0sICJsYWJlbCI6ICJkZWZhdWx0IHJ1bGUifV0sICJjb25maWd1cmF0aW9ucyI6IHt9LCAic2V0cyI6IFtdLCAiaW1wcmVzc2lvbnNEaXNhYmxlZCI6IGZhbHNlfQ=="}'), 'test') } + let(:event_rb_segment_update) { SplitIoClient::SSE::EventSource::StreamData.new("data", 12345, JSON.parse('{"type":"RB_SEGMENT_UPDATE","changeNumber":5564531221,"pcn":1234,"c":0,"d":"eyJjaGFuZ2VOdW1iZXIiOiA1LCAibmFtZSI6ICJzYW1wbGVfcnVsZV9iYXNlZF9zZWdtZW50IiwgInN0YXR1cyI6ICJBQ1RJVkUiLCAidHJhZmZpY1R5cGVOYW1lIjogInVzZXIiLCAiZXhjbHVkZWQiOiB7ImtleXMiOiBbIm1hdXJvQHNwbGl0LmlvIl0sICJzZWdtZW50cyI6IFtdfSwgImNvbmRpdGlvbnMiOiBbeyJtYXRjaGVyR3JvdXAiOiB7ImNvbWJpbmVyIjogIkFORCIsICJtYXRjaGVycyI6IFt7ImtleVNlbGVjdG9yIjogeyJ0cmFmZmljVHlwZSI6ICJ1c2VyIiwgImF0dHJpYnV0ZSI6ICJlbWFpbCJ9LCAibWF0Y2hlclR5cGUiOiAiRU5EU19XSVRIIiwgIm5lZ2F0ZSI6IGZhbHNlLCAid2hpdGVsaXN0TWF0Y2hlckRhdGEiOiB7IndoaXRlbGlzdCI6IFsiQHNwbGl0LmlvIl19fV19fV19"}'), 'test') } context 'add change number to queue' do let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])} let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, telemetry_runtime_producer) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } + let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer) } let(:segment_fetcher) { SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, telemetry_runtime_producer) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } let(:synchronizer) do @@ -36,7 +39,8 @@ repositories = { splits: splits_repository, - segments: segments_repository + segments: segments_repository, + rule_based_segments: rule_based_segments_repository } params = { @@ -51,49 +55,49 @@ end it 'add change number - must tigger fetch - with retries' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').to_return(status: 200, body: '{"splits": [],"since": -1,"till": 1506703262918}') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918').to_return(status: 200, body: '{"splits": [],"since": 1506703262918,"till": 1506703262918}') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918&till=1506703262919').to_return(status: 200, body: '{"splits": [],"since": 1506703262919,"till": 1506703262919}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": -1,"t": 1506703262918}, "rbs":{"d":[],"s":-1,"t":-1}}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": 1506703262918,"t": 1506703262918}, "rbs":{"d":[],"s":-1,"t":-1}}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1&till=1506703262919').to_return(status: 200, body: '{"ff":{"d": [],"s": 1506703262919,"t": 1506703262919}, "rbs":{"d":[],"s":-1,"t":-1}}') - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start worker.add_to_queue(SplitIoClient::SSE::EventSource::StreamData.new("SPLIT_UPDATE", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":1506703262919}'), 'test')) sleep 1 - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918')).to have_been_made.at_least_times(2) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262918&till=1506703262919')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1')).to have_been_made.at_least_times(2) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262918&rbSince=-1&till=1506703262919')).to have_been_made.times(1) end it 'must trigger fetch' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').to_return(status: 200, body: '{"splits": [],"since": -1,"till": 1506703262916}') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916').to_return(status: 200, body: '{"splits": [],"since": 1506703262916,"till": 1506703262918}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": -1,"t": 1506703262916}, "rbs":{"d":[],"s":-1,"t":-1}}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": 1506703262916,"t": 1506703262918}, "rbs":{"d":[],"s":-1,"t":-1}}') - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start worker.add_to_queue(SplitIoClient::SSE::EventSource::StreamData.new("SPLIT_UPDATE", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":1506703262918}'), 'test')) sleep 1 - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1')).to have_been_made.once end it 'must not trigger fetch' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').to_return(status: 200, body: '{"splits": [],"since": -1,"till": 1506703262916}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": -1,"t": 1506703262916}, "rbs":{"d":[],"s":-1,"t":-1}}') - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start worker.add_to_queue(SplitIoClient::SSE::EventSource::StreamData.new("SPLIT_UPDATE", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":1506703262916}'), 'test')) sleep 1 - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916')).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1')).to have_been_made.times(0) end it 'without start, must not fetch' do - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.add_to_queue(SplitIoClient::SSE::EventSource::StreamData.new("SPLIT_UPDATE", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":1506703262918}'), 'test')) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916')).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1')).to have_been_made.times(0) end end @@ -102,16 +106,18 @@ let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, telemetry_runtime_producer) } + let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer) } let(:segment_fetcher) { SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, telemetry_runtime_producer) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:synchronizer) do telemetry_api = SplitIoClient::Api::TelemetryApi.new(config, api_key, telemetry_runtime_producer) impressions_api = SplitIoClient::Api::Impressions.new(api_key, config, telemetry_runtime_producer) repositories = { splits: splits_repository, - segments: segments_repository + segments: segments_repository, + rule_based_segments: rule_based_segments_repository } params = { @@ -136,9 +142,9 @@ end it 'must kill split and trigger fetch' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916').to_return(status: 200, body: '{"splits": [],"since": 1506703262916,"till": 1506703262918}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": 1506703262916,"t": 1506703262918}, "rbs":{"d":[],"s":-1,"t":-1}}') - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start worker.send :kill_feature_flag, SplitIoClient::SSE::EventSource::StreamData.new("SPLIT_KILL", 123, JSON.parse('{"splitName":"FACUNDO_TEST", "defaultTreatment":"on", "type":"SPLIT_KILL","changeNumber":1506703262918}'), 'test') @@ -148,11 +154,11 @@ expect(split[:killed]).to be_truthy expect(split[:defaultTreatment]).to eq('on') expect(split[:changeNumber]).to eq(1_506_703_262_918) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1')).to have_been_made.once end it 'must kill split and must not trigger fetch' do - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start worker.send :kill_feature_flag, SplitIoClient::SSE::EventSource::StreamData.new("SPLIT_KILL", 123, JSON.parse('{"splitName":"FACUNDO_TEST", "defaultTreatment":"on", "type":"SPLIT_KILL","changeNumber":1506703262916}'), 'test') @@ -163,7 +169,7 @@ expect(split[:killed]).to be_truthy expect(split[:defaultTreatment]).to eq('on') expect(split[:changeNumber]).to eq(1_506_703_262_916) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916')).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1')).to have_been_made.times(0) end end @@ -172,16 +178,18 @@ let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new(["set_1"])} let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, telemetry_runtime_producer) } + let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer) } let(:segment_fetcher) { SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, telemetry_runtime_producer) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:synchronizer) do telemetry_api = SplitIoClient::Api::TelemetryApi.new(config, api_key, telemetry_runtime_producer) impressions_api = SplitIoClient::Api::Impressions.new(api_key, config, telemetry_runtime_producer) repositories = { splits: splits_repository, - segments: segments_repository + segments: segments_repository, + rule_based_segments: rule_based_segments_repository } params = { @@ -195,10 +203,10 @@ SplitIoClient::Engine::Synchronizer.new(repositories, config, params) end it 'update split with and without flagset' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1234').to_return(status: 200, body: '{"splits": [],"since": 1234,"till": 1234}') - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1234&till=5564531221").to_return(status: 200, body: '{"splits": [],"since": 1234,"till": 5564531221}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1234&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": 1234,"t": 1234}, "rbs":{"d":[],"s":-1,"t":-1}}') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1234&rbSince=-1&till=5564531221").to_return(status: 200, body: '{"ff":{"d": [],"s": 1234,"t": 5564531221}, "rbs":{"d":[],"s":-1,"t":-1}}') stub_request(:get, "https://sdk.split.io/api/segmentChanges/bilal_segment?since=-1").to_return(status: 200, body: "") - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start splits_repository.set_change_number(1234) @@ -224,16 +232,18 @@ let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, telemetry_runtime_producer) } + let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer) } let(:segment_fetcher) { SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, telemetry_runtime_producer) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:synchronizer) do telemetry_api = SplitIoClient::Api::TelemetryApi.new(config, api_key, telemetry_runtime_producer) impressions_api = SplitIoClient::Api::Impressions.new(api_key, config, telemetry_runtime_producer) repositories = { splits: splits_repository, - segments: segments_repository + segments: segments_repository, + rule_based_segments: rule_based_segments_repository } params = { @@ -246,9 +256,10 @@ SplitIoClient::Engine::Synchronizer.new(repositories, config, params) end + it 'decode and decompress split update data' do stub_request(:get, "https://sdk.split.io/api/segmentChanges/bilal_segment?since=-1").to_return(status: 200, body: "") - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start splits_repository.set_change_number(1234) @@ -276,9 +287,9 @@ end it 'instant ff update split notification with segment matcher.' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1234').to_return(status: 200, body: '{"splits": [],"since": 1234,"till": 1234}') - stub_request(:get, 'https://sdk.split.io/api/segmentChanges/maur-2?since=-1').to_return(status: 200, body: '{"name":"maur-2","added":["admin"],"removed":[],"since":-1,"till":-1}') - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1234&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": 1234,"t": 1234}, "rbs":{"d":[],"s":-1,"t":-1}}') + stub_request(:get, 'https://sdk.split.io/api/segmentChanges/maur-2?since=-1').to_return(status: 200, body: '{"name":"maur-2","added":["admin"],"removed":[],"since":-1,"till":-1}}') + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start splits_repository.set_change_number(1234) @@ -289,9 +300,55 @@ expect(segments_repository.used_segment_names[0]).to eq('maur-2') end + it 'instant ff update split notification with rule based segment matcher.' do + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1234&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": 1234,"t": 1234}, "rbs":{"d":[],"s":-1,"t":5564531221}}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=5564531221&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": 5564531221,"t": 5564531221}, "rbs":{"d":[{"{ + "changeNumber": 123, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{ + "keys":["mauro@split.io","gaston@split.io"], + "segments":[] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + }"}],"s":-1,"t":5564531221}}') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=5564531221&rbSince=5564531221').to_return(status: 200, body: '{"ff":{"d": [],"s": 5564531221,"t": 5564531221}, "rbs":{"d":[],"s":5564531221,"t":5564531221}}') + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) + worker.start + + splits_repository.set_change_number(1234) + expect(rule_based_segments_repository.get_rule_based_segment("sample_rule_based_segment") == nil) + + worker.add_to_queue(event_split_update_rb_segments) + sleep 1 + expect(rule_based_segments_repository.get_rule_based_segment("sample_rule_based_segment") != nil) + end + it 'should not update if definition is nil' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1234').to_return(status: 200, body: '{"splits": [],"since": -1,"till": 1506703262918}') - worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1234&rbSince=-1').to_return(status: 200, body: '{"ff":{"d": [],"s": -1,"t": 1506703262918}, "rbs":{"d":[],"s":-1,"t":-1}}') + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start splits_repository.set_change_number(1234) @@ -299,12 +356,23 @@ sleep 1 expect(splits_repository.exists?('bilal_split') == false) end + + it 'process rb segment update' do + worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) + worker.start + + rule_based_segments_repository.set_change_number(1234) + worker.add_to_queue(event_rb_segment_update) + sleep 2 + rb_segment = rule_based_segments_repository.get_rule_based_segment("sample_rule_based_segment") + expect(rb_segment[:name] == 'sample_rule_based_segment') + end end private def mock_split_changes(splits_json) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) end diff --git a/spec/test_data/integrations/splits.json b/spec/test_data/integrations/splits.json index 4361b6bb..9881fda2 100644 --- a/spec/test_data/integrations/splits.json +++ b/spec/test_data/integrations/splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{"ff": { + "d": [ { "impressionsDisabled": false, "trafficTypeName": "account", @@ -2523,6 +2523,7 @@ ] } ], - "since": -1, - "till": 1506703262916 - } \ No newline at end of file + "s": -1, + "t": 1506703262916 + }, "rbs": {"d":[], "t":-1, "s":-1} +} \ No newline at end of file diff --git a/spec/test_data/integrations/splits_push.json b/spec/test_data/integrations/splits_push.json index 833a41a9..72962e8b 100644 --- a/spec/test_data/integrations/splits_push.json +++ b/spec/test_data/integrations/splits_push.json @@ -1,6 +1,6 @@ -{ - "splits": [ - { +{"ff": { + "d": [ + { "impressionsDisabled": false, "trafficTypeName": "user", "name": "push_test", @@ -225,6 +225,7 @@ ] } ], - "since": -1, - "till": 1585948850109 + "s": -1, + "t": 1585948850109 + }, "rbs": {"d":[], "t":-1, "s":-1} } \ No newline at end of file diff --git a/spec/test_data/integrations/splits_push2.json b/spec/test_data/integrations/splits_push2.json index c4f7d93d..701b7880 100644 --- a/spec/test_data/integrations/splits_push2.json +++ b/spec/test_data/integrations/splits_push2.json @@ -1,5 +1,6 @@ -{ - "splits": [], - "since": 1585948850109, - "till": 1585948850110 +{"ff": { + "d": [], + "s": 1585948850109, + "t": 1585948850110 + }, "rbs": {"d":[], "t":-1, "s":-1} } \ No newline at end of file diff --git a/spec/test_data/integrations/splits_push3.json b/spec/test_data/integrations/splits_push3.json index 8984c5ff..bfade789 100644 --- a/spec/test_data/integrations/splits_push3.json +++ b/spec/test_data/integrations/splits_push3.json @@ -1,6 +1,6 @@ -{ - "splits": [ - { +{"ff": { + "d": [ + { "impressionsDisabled": false, "trafficTypeName": "user", "name": "push_test", @@ -87,6 +87,7 @@ ] } ], - "since": 1585948850110, - "till": 1585948850111 + "s": 1585948850110, + "t": 1585948850111 + }, "rbs": {"d":[], "t":-1, "s":-1} } \ No newline at end of file From 62eae92a0e5e31b9d6b22607c118e7fb36cbd563 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 9 May 2025 09:34:08 -0700 Subject: [PATCH 14/44] polish --- .../cache/repositories/rule_based_segments_repository.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb index 5ea0e333..ab1578e7 100644 --- a/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +++ b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb @@ -22,13 +22,11 @@ class RuleBasedSegmentsRepository < Repository booleanMatcherData: nil, stringMatcherData: nil }] - }, - label: "targeting rule type unsupported by sdk" + } }] def initialize(config) super(config) - @tt_cache = {} @adapter = case @config.cache_adapter.class.to_s when 'SplitIoClient::Cache::Adapters::RedisAdapter' SplitIoClient::Cache::Adapters::CacheAdapter.new(@config) @@ -79,8 +77,6 @@ def exists?(name) end def clear - @tt_cache.clear - @adapter.clear(namespace_key) end From 0480e7890dc247e00465a3c2c3e2dbba811596fb Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 9 May 2025 12:19:07 -0700 Subject: [PATCH 15/44] polish --- .../engine/matchers/combining_matcher.rb | 6 ++++- .../matchers/rule_based_segment_matcher.rb | 22 +--------------- .../rule_based_segment_matcher_spec.rb | 25 ++++++++++++++++--- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/combining_matcher.rb b/lib/splitclient-rb/engine/matchers/combining_matcher.rb index 9a54b75a..b3012011 100644 --- a/lib/splitclient-rb/engine/matchers/combining_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/combining_matcher.rb @@ -56,7 +56,11 @@ def eval_and(args) @matchers.all? do |matcher| if match_with_key?(matcher) - matcher.match?(value: args[:matching_key]) + key = args[:value] + if args[:matching_key] != nil + key = args[:matching_key] + end + matcher.match?(value: key) else matcher.match?(args) end diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index 7900fe96..5523657a 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -30,37 +30,17 @@ def match?(args) rule_based_segment[:excluded][:segments].each do |segment| return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], args[:value]) - if segment[:type] == 'rule-based' - return true if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name])[:conditions], args) - end + return false if SplitIoClient::RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, segment[:name], @config).match?(args) end matches = false rule_based_segment[:conditions].each do |c| condition = SplitIoClient::Condition.new(c, @config) next if condition.empty? - matches = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository, @rule_based_segments_repository).match?(args) end @logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matches}") - matches end - - private - - def match_rbs(conditions, args) - conditions.each do |condition| - next if condition.empty? - - return true if Helpers::EvaluatorHelper::matcher_type(SplitIoClient::Condition.new(condition, @config), - @segments_repository, @rule_based_segments_repository).match?( - matching_key: args[:matching_key], - bucketing_key: args[:value], - attributes: args[:attributes] - ) - end - return false - end end end diff --git a/spec/engine/matchers/rule_based_segment_matcher_spec.rb b/spec/engine/matchers/rule_based_segment_matcher_spec.rb index f09b5422..069a84a3 100644 --- a/spec/engine/matchers/rule_based_segment_matcher_spec.rb +++ b/spec/engine/matchers/rule_based_segment_matcher_spec.rb @@ -34,7 +34,26 @@ it 'return true if excluded rb segment is matched' do rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) - rbs = {:name => 'sample_rule_based_segment', :trafficTypeName => 'tt_name_1', :conditions => [], :excluded => {:keys => [], :segments => [{:name => 'no_excludes', :type => 'rule-based'}]}} + rbs = {:name => 'sample_rule_based_segment', :trafficTypeName => 'tt_name_1', :conditions => [{ + :matcherGroup => { + :combiner => "AND", + :matchers => [ + { + :matcherType => "WHITELIST", + :negate => false, + :userDefinedSegmentMatcherData => nil, + :whitelistMatcherData => { + :whitelist => [ + "bilal@split.io", + "bilal" + ] + }, + :unaryNumericMatcherData => nil, + :betweenMatcherData => nil + } + ] + } + }], :excluded => {:keys => [], :segments => [{:name => 'no_excludes', :type => 'rule-based'}]}} rbs2 = {:name => 'no_excludes', :trafficTypeName => 'tt_name_1', :conditions => [{ :matcherGroup => { @@ -60,8 +79,8 @@ rbs_repositoy.update([rbs, rbs2], [], -1) matcher = described_class.new(segments_repository, rbs_repositoy, 'sample_rule_based_segment', config) - expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be true - expect(matcher.match?(value: 'bilal', attributes: {'email': 'bilal'})).to be false + expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be false + expect(matcher.match?(value: 'bilal', attributes: {'email': 'bilal'})).to be true end it 'return true if condition matches' do From 98ba6755b1d7ca13a41433a6c9bad145ebb0716e Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 9 May 2025 12:27:06 -0700 Subject: [PATCH 16/44] polish --- spec/engine/matchers/rule_based_segment_matcher_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/engine/matchers/rule_based_segment_matcher_spec.rb b/spec/engine/matchers/rule_based_segment_matcher_spec.rb index 069a84a3..4f7786e4 100644 --- a/spec/engine/matchers/rule_based_segment_matcher_spec.rb +++ b/spec/engine/matchers/rule_based_segment_matcher_spec.rb @@ -32,7 +32,7 @@ expect(matcher.match?(value: 'key2')).to be false end - it 'return true if excluded rb segment is matched' do + it 'return false if excluded rb segment is matched' do rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) rbs = {:name => 'sample_rule_based_segment', :trafficTypeName => 'tt_name_1', :conditions => [{ :matcherGroup => { From 80cd16a7078f7157d12a9b461e5379551498364f Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 9 May 2025 17:34:09 -0700 Subject: [PATCH 17/44] polish --- .../engine/matchers/rule_based_segment_matcher.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index 5523657a..c3a5f9f8 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -30,7 +30,7 @@ def match?(args) rule_based_segment[:excluded][:segments].each do |segment| return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], args[:value]) - return false if SplitIoClient::RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, segment[:name], @config).match?(args) + return false if segment[:type] == 'rule-based' and SplitIoClient::RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, segment[:name], @config).match?(args) end matches = false From 14cee7326d2c81f0320768610afaadfb3bb0d117 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 14 May 2025 10:40:22 -0700 Subject: [PATCH 18/44] Integrations tests --- .../cache/fetchers/split_fetcher.rb | 1 + lib/splitclient-rb/clients/split_client.rb | 2 + lib/splitclient-rb/engine/api/splits.rb | 19 +- .../matchers/rule_based_segment_matcher.rb | 28 +- lib/splitclient-rb/engine/parser/condition.rb | 2 +- lib/splitclient-rb/engine/parser/evaluator.rb | 2 +- .../helpers/evaluator_helper.rb | 2 +- lib/splitclient-rb/split_factory.rb | 6 +- .../clients/split_client_spec.rb | 3 +- spec/cache/fetchers/segment_fetch_spec.rb | 2 +- spec/engine/auth_api_client_spec.rb | 6 +- spec/engine/matchers/between_matcher_spec.rb | 12 +- .../engine/matchers/combining_matcher_spec.rb | 2 +- spec/engine/matchers/equal_to_matcher_spec.rb | 8 +- .../greater_than_or_equal_to_matcher_spec.rb | 6 +- .../less_than_or_equal_to_matcher_spec.rb | 8 +- .../semver_matchers_integration_spec.rb | 22 +- .../engine/matchers/whitelist_matcher_spec.rb | 2 +- spec/engine/parser/evaluator_spec.rb | 6 +- spec/engine/push_manager_spec.rb | 23 +- spec/engine/sync_manager_spec.rb | 16 +- spec/engine_spec.rb | 18 +- spec/integrations/dedupe_impression_spec.rb | 12 +- spec/integrations/in_memory_client_spec.rb | 129 +++++-- spec/integrations/push_client_spec.rb | 80 ++--- spec/integrations/redis_client_spec.rb | 2 +- spec/splitclient/split_client_spec.rb | 40 ++- spec/splitclient/split_factory_spec.rb | 24 +- spec/splitclient/split_manager_spec.rb | 14 +- spec/splitclient_rb_corner_cases_spec.rb | 4 +- .../rule_base_segments.json | 135 +++++++ .../rule_base_segments2.json | 140 ++++++++ .../rule_base_segments3.json | 108 ++++++ .../rule_based_segments/split_old_spec.json | 328 ++++++++++++++++++ .../datetime_matcher_splits.json | 9 +- .../negate_number_matcher_splits.json | 9 +- .../negative_number_matcher_splits.json | 9 +- .../number_matcher_splits.json | 9 +- .../splits/boolean_matcher/splits.json | 9 +- .../splits/combining_matcher_splits.json | 6 +- .../splits/engine/all_keys_matcher.json | 9 +- .../splits/engine/configurations.json | 9 +- .../splits/engine/dependency_matcher.json | 9 +- .../splits/engine/equal_to_set_matcher.json | 11 +- .../splits/engine/impressions_test.json | 9 +- spec/test_data/splits/engine/killed.json | 9 +- .../engine/segment_deleted_matcher.json | 9 +- .../splits/engine/segment_matcher.json | 9 +- .../splits/engine/segment_matcher2.json | 9 +- .../splits/engine/whitelist_matcher.json | 9 +- .../splits/equal_to_matcher/date_splits.json | 9 +- .../equal_to_matcher/negative_splits.json | 9 +- .../splits/equal_to_matcher/splits.json | 9 +- .../splits/equal_to_matcher/zero_splits.json | 9 +- .../date_splits.json | 9 +- .../negative_splits.json | 9 +- .../splits.json | 9 +- spec/test_data/splits/imp-toggle.json | 6 +- .../date_splits.json | 9 +- .../date_splits2.json | 9 +- .../negative_splits.json | 9 +- .../less_than_or_equal_to_matcher/splits.json | 9 +- .../semver_matchers/semver_between.json | 7 +- .../semver_matchers/semver_equalto.json | 7 +- .../semver_greater_or_equalto.json | 7 +- .../splits/semver_matchers/semver_inlist.json | 7 +- .../semver_less_or_equalto.json | 7 +- spec/test_data/splits/splits4.json | 9 +- .../splits/splits_traffic_allocation.json | 5 +- ...splits_traffic_allocation_one_percent.json | 7 +- .../splits/whitelist_matcher_splits.json | 7 +- 71 files changed, 1212 insertions(+), 311 deletions(-) create mode 100644 spec/test_data/rule_based_segments/rule_base_segments.json create mode 100644 spec/test_data/rule_based_segments/rule_base_segments2.json create mode 100644 spec/test_data/rule_based_segments/rule_base_segments3.json create mode 100644 spec/test_data/rule_based_segments/split_old_spec.json diff --git a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb index 8353d51d..c2772d62 100644 --- a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +++ b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb @@ -28,6 +28,7 @@ def fetch_splits(fetch_options = { cache_control_headers: false, till: nil }) SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config) SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config) @splits_repository.set_segment_names(data[:segment_names]) + @rule_based_segments_repository.set_segment_names(data[:segment_names]) @config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled { segment_names: data[:segment_names], success: true } diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index b81fbfb2..ed0e1021 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -22,6 +22,7 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage @api_key = sdk_key @splits_repository = repositories[:splits] @segments_repository = repositories[:segments] + @rule_based_segments_repository = repositories[:rule_based_segments] @impressions_repository = repositories[:impressions] @events_repository = repositories[:events] @status_manager = status_manager @@ -115,6 +116,7 @@ def destroy @splits_repository.clear @segments_repository.clear + @rule_based_segments_repository.clear SplitIoClient.load_factory_registry SplitIoClient.split_factory_registry.remove(@api_key) diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index 62e3d4a6..0596c9de 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -33,7 +33,7 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till @config.split_logger.log_if_transport("Feature flag changes response: #{result[:ff].to_s}") unless result[:rbs][:d].empty? - @config.split_logger.log_if_debug("#{result[:ff][:d].length} rule based segments retrieved. since=#{since_rbs}") + @config.split_logger.log_if_debug("#{result[:rbs][:d].length} rule based segments retrieved. since=#{since_rbs}") end @config.split_logger.log_if_transport("rule based segments changes response: #{result[:rbs].to_s}") @@ -56,15 +56,24 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till def objects_with_segment_names(objects_json) parsed_objects = JSON.parse(objects_json, symbolize_names: true) + parsed_objects[:segment_names] = Set.new parsed_objects[:segment_names] = parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits| splits << Helpers::Util.segment_names_by_object(split, "IN_SEGMENT") end.flatten - if not parsed_objects[:ff][:rbs].nil? - parsed_objects[:segment_names].merge parsed_objects[:ff][:rbs].each_with_object(Set.new) do |rule_based_segment, rule_based_segments| - rule_based_segments << Helpers::Util.segment_names_by_object(rule_based_segment, "IN_SEGMENT") - end.flatten + + parsed_objects[:rbs][:d].each do |rule_based_segment| + parsed_objects[:segment_names].merge Helpers::Util.segment_names_by_object(rule_based_segment, "IN_SEGMENT") + end + + parsed_objects[:rbs][:d].each do |rule_based_segment| + rule_based_segment[:excluded][:segments].each do |segment| + if segment[:type] == "standard" + parsed_objects[:segment_names].add(segment[:name]) + end + end end + parsed_objects end end diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index 7900fe96..69c72272 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -25,13 +25,18 @@ def match?(args) rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name) return false if rule_based_segment.nil? - return false if rule_based_segment[:excluded][:keys].include?([args[:value]]) + if args[:value].nil? or args[:value].empty? + key = args[:matching_key] + else + key = args[:value] + end + return false if rule_based_segment[:excluded][:keys].include?(key) rule_based_segment[:excluded][:segments].each do |segment| - return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], args[:value]) + return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], key) if segment[:type] == 'rule-based' - return true if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name])[:conditions], args) + return false if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args) end end @@ -49,18 +54,13 @@ def match?(args) private - def match_rbs(conditions, args) - conditions.each do |condition| - next if condition.empty? - - return true if Helpers::EvaluatorHelper::matcher_type(SplitIoClient::Condition.new(condition, @config), - @segments_repository, @rule_based_segments_repository).match?( - matching_key: args[:matching_key], - bucketing_key: args[:value], - attributes: args[:attributes] + def match_rbs(rule_based_segment, args) + rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, rule_based_segment[:name], @config) + return rbs_matcher.match?( + matching_key: args[:matching_key], + bucketing_key: args[:value], + attributes: args[:attributes] ) - end - return false end end end diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index 926dd206..0caaabe8 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -253,7 +253,7 @@ def negate # @return [void] def set_partitions partitions_list = [] - return partitions_list unless @data.key?('partitions') + return partitions_list unless @data.key?(:partitions) or @data.key?('partitions') @data[:partitions].each do |p| partition = SplitIoClient::Partition.new(p) diff --git a/lib/splitclient-rb/engine/parser/evaluator.rb b/lib/splitclient-rb/engine/parser/evaluator.rb index b9508e38..ec7636cd 100644 --- a/lib/splitclient-rb/engine/parser/evaluator.rb +++ b/lib/splitclient-rb/engine/parser/evaluator.rb @@ -5,6 +5,7 @@ class Evaluator def initialize(segments_repository, splits_repository, rb_segment_repository, config) @splits_repository = splits_repository @segments_repository = segments_repository + @rb_segment_repository = rb_segment_repository @config = config end @@ -58,7 +59,6 @@ def match(split, keys, attributes) in_rollout = true end - condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository, @rb_segment_repository).match?( matching_key: keys[:matching_key], bucketing_key: keys[:bucketing_key], diff --git a/lib/splitclient-rb/helpers/evaluator_helper.rb b/lib/splitclient-rb/helpers/evaluator_helper.rb index e9c55cfc..331c7987 100644 --- a/lib/splitclient-rb/helpers/evaluator_helper.rb +++ b/lib/splitclient-rb/helpers/evaluator_helper.rb @@ -8,7 +8,7 @@ def self.matcher_type(condition, segments_repository, rb_segment_repository) segments_repository.adapter.pipelined do condition.matchers.each do |matcher| matchers << if matcher[:negate] - condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher)) + condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository)) else matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository) end diff --git a/lib/splitclient-rb/split_factory.rb b/lib/splitclient-rb/split_factory.rb index 22be6ca7..7c8a4306 100644 --- a/lib/splitclient-rb/split_factory.rb +++ b/lib/splitclient-rb/split_factory.rb @@ -55,7 +55,7 @@ def initialize(api_key, config_hash = {}) @status_manager = Engine::StatusManager.new(@config) @split_validator = SplitIoClient::Validators.new(@config) - @evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @config) + @evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @rule_based_segment_repository, @config) start! @@ -179,7 +179,7 @@ def build_telemetry_components end def build_fetchers - @split_fetcher = SplitFetcher.new(@splits_repository, @rule_based_segments_repository, @api_key, @config, @runtime_producer) + @split_fetcher = SplitFetcher.new(@splits_repository, @rule_based_segment_repository, @api_key, @config, @runtime_producer) @segment_fetcher = SegmentFetcher.new(@segments_repository, @api_key, @config, @runtime_producer) end @@ -221,7 +221,7 @@ def build_repositories end @splits_repository = SplitsRepository.new(@config, @flag_sets_repository, @flag_sets_filter) @segments_repository = SegmentsRepository.new(@config) - @rule_based_segment_repository = RuleBasedSegmentRepository.new(@config) + @rule_based_segment_repository = RuleBasedSegmentsRepository.new(@config) @impressions_repository = ImpressionsRepository.new(@config) @events_repository = EventsRepository.new(@config, @api_key, @runtime_producer) end diff --git a/spec/allocations/splitclient-rb/clients/split_client_spec.rb b/spec/allocations/splitclient-rb/clients/split_client_spec.rb index 0dd15231..5d38f377 100644 --- a/spec/allocations/splitclient-rb/clients/split_client_spec.rb +++ b/spec/allocations/splitclient-rb/clients/split_client_spec.rb @@ -10,6 +10,7 @@ let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } let(:impressions_repository) { SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:impression_counter) { SplitIoClient::Engine::Common::ImpressionCounter.new } let(:evaluation_producer) { SplitIoClient::Telemetry::EvaluationProducer.new(config) } let(:impression_observer) { SplitIoClient::Observers::ImpressionObserver.new } @@ -19,7 +20,7 @@ let(:api_key) { 'SplitClient-key' } let(:telemetry_api) { SplitIoClient::Api::TelemetryApi.new(config, api_key, runtime_producer) } let(:impressions_api) { SplitIoClient::Api::Impressions.new(api_key, config, runtime_producer) } - let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, config) } + let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) } let(:sender_adapter) do SplitIoClient::Cache::Senders::ImpressionsSenderAdapter.new(config, telemetry_api, diff --git a/spec/cache/fetchers/segment_fetch_spec.rb b/spec/cache/fetchers/segment_fetch_spec.rb index 031bb2a8..6280866b 100644 --- a/spec/cache/fetchers/segment_fetch_spec.rb +++ b/spec/cache/fetchers/segment_fetch_spec.rb @@ -26,7 +26,7 @@ stub_request(:get, 'https://sdk.split.io/api/segmentChanges/employees?since=1473863075059') .to_return(status: 200, body: segments_json2) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_with_segments_json) end diff --git a/spec/engine/auth_api_client_spec.rb b/spec/engine/auth_api_client_spec.rb index 0235c130..eaa5ba70 100644 --- a/spec/engine/auth_api_client_spec.rb +++ b/spec/engine/auth_api_client_spec.rb @@ -15,7 +15,7 @@ let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } it 'authenticate success' do - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 200, body: body_response) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 200, body: body_response) auth_api_client = subject.new(config, telemetry_runtime_producer) response = auth_api_client.authenticate(api_key) @@ -26,7 +26,7 @@ end it 'auth server return 500' do - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 500) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 500) auth_api_client = subject.new(config, telemetry_runtime_producer) response = auth_api_client.authenticate(api_key) @@ -36,7 +36,7 @@ end it 'auth server return 401' do - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 401) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 401) auth_api_client = subject.new(config, telemetry_runtime_producer) response = auth_api_client.authenticate(api_key) diff --git a/spec/engine/matchers/between_matcher_spec.rb b/spec/engine/matchers/between_matcher_spec.rb index 9e16e550..765ea2d6 100644 --- a/spec/engine/matchers/between_matcher_spec.rb +++ b/spec/engine/matchers/between_matcher_spec.rb @@ -41,9 +41,7 @@ before do # stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?since/) # .to_return(status: 200, body: number_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges') - .to_return(status: 200, body: number_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: number_matcher_splits) subject.block_until_ready sleep 1 @@ -69,7 +67,9 @@ let(:non_matching_low_value_negative_attributes) { { income: -999 } } before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since.*/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since.*/) + .to_return(status: 200, body: negative_number_matcher_splits) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: negative_number_matcher_splits) subject.block_until_ready sleep 1 @@ -97,7 +97,9 @@ let(:non_matching_high_value_attributes) { { created: 1_459_775_460 } } # "2016/04/04T13:11Z" before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since/) + .to_return(status: 200, body: datetime_matcher_splits) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: datetime_matcher_splits) subject.block_until_ready sleep 1 diff --git a/spec/engine/matchers/combining_matcher_spec.rb b/spec/engine/matchers/combining_matcher_spec.rb index bdaccaa7..ab04b2a3 100644 --- a/spec/engine/matchers/combining_matcher_spec.rb +++ b/spec/engine/matchers/combining_matcher_spec.rb @@ -19,7 +19,7 @@ before do stub_request(:get, 'https://sdk.split.io/api/segmentChanges/employees?since=-1') .to_return(status: 200, body: segments_json) - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1") .to_return(status: 200, body: splits_json) stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') diff --git a/spec/engine/matchers/equal_to_matcher_spec.rb b/spec/engine/matchers/equal_to_matcher_spec.rb index 043b39df..6f249c6e 100644 --- a/spec/engine/matchers/equal_to_matcher_spec.rb +++ b/spec/engine/matchers/equal_to_matcher_spec.rb @@ -37,7 +37,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: 'ok') @@ -64,7 +64,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: zero_splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: 'ok') @@ -90,7 +90,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: negative_splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: 'ok') @@ -117,7 +117,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: date_splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: 'ok') diff --git a/spec/engine/matchers/greater_than_or_equal_to_matcher_spec.rb b/spec/engine/matchers/greater_than_or_equal_to_matcher_spec.rb index 5283bfb9..9503b5c7 100644 --- a/spec/engine/matchers/greater_than_or_equal_to_matcher_spec.rb +++ b/spec/engine/matchers/greater_than_or_equal_to_matcher_spec.rb @@ -35,7 +35,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) @@ -61,7 +61,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: negative_splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) @@ -92,7 +92,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: date_splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) diff --git a/spec/engine/matchers/less_than_or_equal_to_matcher_spec.rb b/spec/engine/matchers/less_than_or_equal_to_matcher_spec.rb index 60936dc0..13bee2cd 100644 --- a/spec/engine/matchers/less_than_or_equal_to_matcher_spec.rb +++ b/spec/engine/matchers/less_than_or_equal_to_matcher_spec.rb @@ -37,7 +37,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) @@ -64,7 +64,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: negative_splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) @@ -93,7 +93,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: date_splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) @@ -119,7 +119,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: date_splits2_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) diff --git a/spec/engine/matchers/semver_matchers_integration_spec.rb b/spec/engine/matchers/semver_matchers_integration_spec.rb index 86909af5..04a76af4 100644 --- a/spec/engine/matchers/semver_matchers_integration_spec.rb +++ b/spec/engine/matchers/semver_matchers_integration_spec.rb @@ -51,11 +51,11 @@ context 'equal to matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since\.*/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since\.*/) .to_return(status: 200, body: semver_equalto_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: semver_equalto_matcher_splits) - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1675259356568") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1675259356568&rbSince=-1") .to_return(status: 200, body: semver_equalto_matcher_splits) sleep 1 subject.block_until_ready @@ -75,9 +75,9 @@ context 'greater than or equal to matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since/) .to_return(status: 200, body: semver_greater_or_equalto_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: semver_greater_or_equalto_matcher_splits) sleep 1 subject.block_until_ready @@ -98,9 +98,9 @@ context 'less than or equal to matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since=-1&rbSince=-1/) .to_return(status: 200, body: semver_less_or_equalto_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: semver_less_or_equalto_matcher_splits) sleep 1 subject.block_until_ready @@ -121,9 +121,9 @@ context 'in list matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since=-1&rbSince=-1/) .to_return(status: 200, body: semver_inlist_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: semver_inlist_matcher_splits) sleep 1 subject.block_until_ready @@ -144,9 +144,9 @@ context 'between matcher' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since=-1&rbSince=-1/) .to_return(status: 200, body: semver_between_matcher_splits) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: semver_between_matcher_splits) sleep 1 subject.block_until_ready diff --git a/spec/engine/matchers/whitelist_matcher_spec.rb b/spec/engine/matchers/whitelist_matcher_spec.rb index 25f4a4ba..90f40f65 100644 --- a/spec/engine/matchers/whitelist_matcher_spec.rb +++ b/spec/engine/matchers/whitelist_matcher_spec.rb @@ -22,7 +22,7 @@ before do stub_request(:any, /https:\/\/telemetry.*/) .to_return(status: 200, body: 'ok') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) stub_request(:any, /https:\/\/events.*/) .to_return(status: 200, body: "", headers: {}) diff --git a/spec/engine/parser/evaluator_spec.rb b/spec/engine/parser/evaluator_spec.rb index 99f5cdfb..a6beabe9 100644 --- a/spec/engine/parser/evaluator_spec.rb +++ b/spec/engine/parser/evaluator_spec.rb @@ -32,12 +32,12 @@ context 'dependency matcher' do it 'uses cache' do allow(evaluator.instance_variable_get(:@splits_repository)) - .to receive(:get_split).and_return(split_data[:splits][0]) + .to receive(:get_split).and_return(split_data[:ff][:d][0]) expect(evaluator).to receive(:match).exactly(2).times - evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, split_data[:splits][0]) + evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, split_data[:ff][:d][0]) - evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, split_data[:splits][1]) + evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, split_data[:ff][:d][1]) end end end diff --git a/spec/engine/push_manager_spec.rb b/spec/engine/push_manager_spec.rb index a1a6d9e7..4cc3f9db 100644 --- a/spec/engine/push_manager_spec.rb +++ b/spec/engine/push_manager_spec.rb @@ -14,14 +14,15 @@ let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } - let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, runtime_producer) } + let(:split_fetcher) { SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, runtime_producer) } let(:segment_fetcher) { SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, runtime_producer) } - let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(split_fetcher, config, splits_repository, runtime_producer, segment_fetcher) } + let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(split_fetcher, config, splits_repository, runtime_producer, segment_fetcher, rule_based_segments_repository) } let(:segments_worker) { SplitIoClient::SSE::Workers::SegmentsWorker.new(segment_fetcher, config, segments_repository) } let(:push_status_queue) { Queue.new } let(:notification_manager_keeper) { SplitIoClient::SSE::NotificationManagerKeeper.new(config, runtime_producer, push_status_queue) } - let(:repositories) { { splits: splits_repository, segments: segments_repository } } + let(:repositories) { { splits: splits_repository, segments: segments_repository, rule_based_segments: rule_based_segments_repository } } let(:impression_counter) { SplitIoClient::Engine::Common::ImpressionCounter.new } let(:params) do { @@ -43,7 +44,7 @@ send_mock_content(res, 'content') end - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 200, body: body_response) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 200, body: body_response) config.streaming_service_url = server.base_uri sse_handler = SplitIoClient::SSE::SSEHandler.new(config, splits_worker, segments_worker, sse_client) @@ -51,7 +52,7 @@ push_manager = subject.new(config, sse_handler, api_key, runtime_producer) connected = push_manager.start_sse - expect(a_request(:get, config.auth_service_url + "?s=1.1")).to have_been_made.times(1) + expect(a_request(:get, config.auth_service_url + "?s=1.3")).to have_been_made.times(1) sleep(1.5) expect(connected).to eq(true) @@ -61,14 +62,14 @@ end it 'must not connect to server. Auth server return 500' do - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 500) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 500) sse_handler = SplitIoClient::SSE::SSEHandler.new(config, splits_worker, segments_worker, sse_client) push_manager = subject.new(config, sse_handler, api_key, runtime_producer) connected = push_manager.start_sse - expect(a_request(:get, config.auth_service_url + "?s=1.1")).to have_been_made.times(1) + expect(a_request(:get, config.auth_service_url + "?s=1.3")).to have_been_made.times(1) sleep(1.5) @@ -77,14 +78,14 @@ end it 'must not connect to server. Auth server return 401' do - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 401) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 401) sse_handler = SplitIoClient::SSE::SSEHandler.new(config, splits_worker, segments_worker, sse_client) push_manager = subject.new(config, sse_handler, api_key, runtime_producer) connected = push_manager.start_sse - expect(a_request(:get, config.auth_service_url + "?s=1.1")).to have_been_made.times(1) + expect(a_request(:get, config.auth_service_url + "?s=1.3")).to have_been_made.times(1) sleep(1.5) @@ -100,14 +101,14 @@ send_mock_content(res, 'content') end - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 200, body: body_response) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 200, body: body_response) config.streaming_service_url = server.base_uri sse_handler = SplitIoClient::SSE::SSEHandler.new(config, splits_worker, segments_worker, sse_client) push_manager = subject.new(config, sse_handler, api_key, runtime_producer) connected = push_manager.start_sse - expect(a_request(:get, config.auth_service_url + "?s=1.1")).to have_been_made.times(1) + expect(a_request(:get, config.auth_service_url + "?s=1.3")).to have_been_made.times(1) sleep(1.5) diff --git a/spec/engine/sync_manager_spec.rb b/spec/engine/sync_manager_spec.rb index d472de01..68169a7c 100644 --- a/spec/engine/sync_manager_spec.rb +++ b/spec/engine/sync_manager_spec.rb @@ -20,6 +20,7 @@ let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:impressions_repository) { SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) } let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } let(:events_repository) { SplitIoClient::Cache::Repositories::EventsRepository.new(config, api_key, telemetry_runtime_producer) } @@ -29,12 +30,13 @@ splits: splits_repository, segments: segments_repository, impressions: impressions_repository, - events: events_repository + events: events_repository, + rule_based_segments: rule_based_segments_repository } end let(:sync_params) do { - split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, api_key, config, telemetry_runtime_producer), + split_fetcher: SplitIoClient::Cache::Fetchers::SplitFetcher.new(splits_repository, rule_based_segments_repository, api_key, config, telemetry_runtime_producer), segment_fetcher: SplitIoClient::Cache::Fetchers::SegmentFetcher.new(segments_repository, api_key, config, telemetry_runtime_producer), imp_counter: impression_counter, telemetry_runtime_producer: telemetry_runtime_producer, @@ -50,7 +52,7 @@ let(:telemetry_api) { SplitIoClient::Api::TelemetryApi.new(config, api_key, telemetry_runtime_producer) } let(:telemetry_synchronizer) { SplitIoClient::Telemetry::Synchronizer.new(config, telemetry_consumers, init_producer, repositories, telemetry_api, 0, 0) } let(:status_manager) { SplitIoClient::Engine::StatusManager.new(config) } - let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, splits_repository, telemetry_runtime_producer, sync_params[:segment_fetcher]) } + let(:splits_worker) { SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, splits_repository, telemetry_runtime_producer, sync_params[:segment_fetcher], rule_based_segments_repository) } let(:segments_worker) { SplitIoClient::SSE::Workers::SegmentsWorker.new(synchronizer, config, segments_repository) } let(:notification_processor) { SplitIoClient::SSE::NotificationProcessor.new(config, splits_worker, segments_worker) } let(:event_parser) { SplitIoClient::SSE::EventSource::EventParser.new(config) } @@ -68,7 +70,7 @@ mock_segment_changes('segment2', segment2, '-1') mock_segment_changes('segment2', segment2, '1470947453878') mock_segment_changes('segment3', segment3, '-1') - stub_request(:get, config.auth_service_url + "?s=1.1").to_return(status: 200, body: body_response) + stub_request(:get, config.auth_service_url + "?s=1.3").to_return(status: 200, body: body_response) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') end @@ -84,7 +86,7 @@ sync_manager.start sleep(2) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.once expect(config.threads.size).to eq(11) config.threads.values.each { |thread| Thread.kill(thread) } @@ -104,7 +106,7 @@ sync_manager.start sleep(2) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.once + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.once expect(config.threads.size).to eq(8) config.threads.values.each { |thread| Thread.kill(thread) } @@ -136,7 +138,7 @@ private def mock_split_changes_with_since(splits_json, since) - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=#{since}") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=#{since}&rbSince=-1") .to_return(status: 200, body: splits_json) end diff --git a/spec/engine_spec.rb b/spec/engine_spec.rb index b9999044..7909d64d 100644 --- a/spec/engine_spec.rb +++ b/spec/engine_spec.rb @@ -46,7 +46,7 @@ @mode = cache_adapter.equal?(:memory) ? :standalone : :consumer stub_request(:any, /https:\/\/telemetry.*/).to_return(status: 200, body: 'ok') stub_request(:any, /https:\/\/events.*/).to_return(status: 200, body: '') - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since=-1&rbSince=-1/) .to_return(status: 200, body: '') stub_request(:post, "https://telemetry.split.io/api/v1/metrics/config") .to_return(status: 200, body: '') @@ -128,7 +128,7 @@ context '#get_treatment' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/).to_return(status: 200, body: '') + stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.3&since=-1&rbSince=-1/).to_return(status: 200, body: '') load_splits(all_keys_matcher_json, flag_sets_json) subject.block_until_ready @@ -928,7 +928,7 @@ end it 'returns control' do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1") .to_return(status: 200, body: all_keys_matcher_json) subject.block_until_ready @@ -942,7 +942,7 @@ describe 'redis outage' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1") .to_return(status: 200, body: all_keys_matcher_json) end @@ -955,7 +955,7 @@ describe 'events' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1") .to_return(status: 200, body: all_keys_matcher_json) subject.block_until_ready end @@ -985,7 +985,7 @@ context '#track' do before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since/) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1") .to_return(status: 200, body: all_keys_matcher_json) subject.block_until_ready end @@ -1192,7 +1192,7 @@ end before do - stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since.*/) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1") .to_return(status: 200, body: all_keys_matcher_json) end @@ -1270,7 +1270,7 @@ def load_splits(splits_json, flag_sets_json) if @mode.equal?(:standalone) # stub_request(:get, /https:\/\/sdk\.split\.io\/api\/splitChanges\?s=1\.1&since.*/) - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=-1") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1") .to_return(status: 200, body: splits_json) else add_splits_to_repository(splits_json) @@ -1288,7 +1288,7 @@ def load_segments(segments_json) end def add_splits_to_repository(splits_json) - splits = JSON.parse(splits_json, symbolize_names: true)[:splits] + splits = JSON.parse(splits_json, symbolize_names: true)[:ff][:d] splits_repository = subject.instance_variable_get(:@splits_repository) diff --git a/spec/integrations/dedupe_impression_spec.rb b/spec/integrations/dedupe_impression_spec.rb index 399d3045..86e6b935 100644 --- a/spec/integrations/dedupe_impression_spec.rb +++ b/spec/integrations/dedupe_impression_spec.rb @@ -33,8 +33,8 @@ context 'checking logic impressions' do it 'get_treament should post 7 impressions - debug mode' do stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916').to_return(status: 200, body: '') - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=-1").to_return(status: 200, body: splits, headers: {}) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916').to_return(status: 200, body: '') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1").to_return(status: 200, body: splits, headers: {}) factory = SplitIoClient::SplitFactory.new('test_api_key_debug-1', streaming_enabled: false, impressions_mode: :debug) debug_client = factory.client @@ -61,7 +61,7 @@ it 'get_treaments should post 9 impressions - debug mode' do stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1').to_return(status: 200, body: '') factory = SplitIoClient::SplitFactory.new('test_api_key_debug-2', streaming_enabled: false, impressions_mode: :debug) debug_client = factory.client @@ -81,7 +81,7 @@ it 'get_treament should post 3 impressions - optimized mode' do stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1').to_return(status: 200, body: '') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/usage').to_return(status: 200, body: '') factory = SplitIoClient::SplitFactory.new('test_api_key-1', streaming_enabled: false, impressions_mode: :optimized, impressions_refresh_rate: 60) @@ -113,7 +113,7 @@ it 'get_treaments should post 8 impressions - optimized mode' do stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/usage').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1').to_return(status: 200, body: '') factory = SplitIoClient::SplitFactory.new('test_api_key-2', streaming_enabled: false, impressions_mode: :optimized) client = factory.client @@ -149,7 +149,7 @@ private def mock_split_changes_v2(splits_json) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) end diff --git a/spec/integrations/in_memory_client_spec.rb b/spec/integrations/in_memory_client_spec.rb index d70e6c76..ab0538ae 100644 --- a/spec/integrations/in_memory_client_spec.rb +++ b/spec/integrations/in_memory_client_spec.rb @@ -52,7 +52,7 @@ context '#get_treatment' do it 'returns CONTROL when server return 500' do - # stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + # stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916").to_return(status: 200, body: 'ok') mock_split_changes_error expect(client.get_treatment('nico_test', 'FACUNDO_TEST')).to eq 'control' @@ -67,7 +67,7 @@ end it 'returns treatments with FACUNDO_TEST feature and check impressions' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready expect(client.get_treatment('nico_test', 'FACUNDO_TEST')).to eq 'on' expect(client.get_treatment('mauro_test', 'FACUNDO_TEST')).to eq 'off' @@ -91,7 +91,7 @@ end it 'returns treatments with Test_Save_1 feature and check impressions' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready expect(client.get_treatment('1', 'Test_Save_1')).to eq 'on' expect(client.get_treatment('24', 'Test_Save_1')).to eq 'off' @@ -115,7 +115,7 @@ end it 'returns treatments with input validations' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready expect(client.get_treatment('nico_test', 'FACUNDO_TEST')).to eq 'on' expect(client.get_treatment('', 'FACUNDO_TEST')).to eq 'control' @@ -143,7 +143,7 @@ end it 'returns CONTROL with treatment doesnt exist' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') expect(client.get_treatment('nico_test', 'random_treatment')).to eq 'control' impressions = custom_impression_listener.queue @@ -151,7 +151,7 @@ end it 'with multiple factories returns on' do -# stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') +# stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916").to_return(status: 200, body: 'ok') local_log = StringIO.new logger = Logger.new(local_log) @@ -231,7 +231,7 @@ context '#get_treatment_with_config' do it 'returns treatments and configs with FACUNDO_TEST treatment and check impressions' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready expect(client.get_treatment_with_config('nico_test', 'FACUNDO_TEST')).to eq( treatment: 'on', @@ -261,7 +261,7 @@ end it 'returns treatments and configs with MAURO_TEST treatment and check impressions' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready expect(client.get_treatment_with_config('mauro', 'MAURO_TEST')).to eq( @@ -292,7 +292,7 @@ end it 'returns treatments with input validations' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready expect(client.get_treatment_with_config('nico_test', 'FACUNDO_TEST')).to eq( @@ -339,7 +339,7 @@ end it 'returns CONTROL with treatment doesnt exist' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready expect(client.get_treatment_with_config('nico_test', 'random_treatment')).to eq( @@ -353,7 +353,7 @@ end it 'returns CONTROL when server return 500' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') mock_split_changes_error expect(client.get_treatment_with_config('nico_test', 'FACUNDO_TEST')).to eq( @@ -375,7 +375,7 @@ context '#get_treatments' do before do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') end it 'returns treatments and check impressions' do @@ -479,7 +479,8 @@ context '#get_treatments_by_flag_set' do before do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + mock_split_changes(splits) + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') end it 'returns treatments and check impressions' do @@ -564,7 +565,7 @@ context '#get_treatments_by_flag_sets' do before do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') end it 'returns treatments and check impressions' do @@ -647,7 +648,8 @@ context '#get_treatments_with_config_by_flag_set' do before do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') + stub_request(:post, "https://events.split.io/api/testImpressions/bulk").to_return(status: 200, body: "", headers: {}) end it 'returns treatments and check impressions' do @@ -757,7 +759,7 @@ context '#get_treatments_with_config_by_flag_sets' do before do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') end it 'returns treatments and check impressions' do @@ -861,7 +863,7 @@ context '#get_treatments_with_config' do it 'returns treatments and check impressions' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready result = client.get_treatments_with_config('nico_test', %w[FACUNDO_TEST MAURO_TEST Test_Save_1]) expect(result[:FACUNDO_TEST]).to eq( @@ -901,7 +903,7 @@ end it 'returns treatments with input validation' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready result1 = client.get_treatments_with_config('nico_test', %w[FACUNDO_TEST "" nil]) result2 = client.get_treatments_with_config('', %w["" MAURO_TEST Test_Save_1]) @@ -940,7 +942,7 @@ end it 'returns CONTROL with treatment doesnt exist' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready result = client.get_treatments_with_config('nico_test', %w[FACUNDO_TEST random_treatment]) @@ -965,7 +967,7 @@ end it 'returns CONTROL when server return 500' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') mock_split_changes_error result = client.get_treatments_with_config('nico_test', %w[FACUNDO_TEST MAURO_TEST Test_Save_1]) @@ -1085,7 +1087,7 @@ context '#track' do it 'returns true' do stub_request(:post, 'https://events.split.io/api/events/bulk').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1').to_return(status: 200, body: '') properties = { property_1: 1, @@ -1144,7 +1146,7 @@ flag_sets_filter: ['set_3', '@3we']) end before do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1&sets=set_3') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_3') .to_return(status: 200, body: splits) mock_segment_changes('segment1', segment1, '-1') mock_segment_changes('segment1', segment1, '1470947453877') @@ -1204,7 +1206,7 @@ context 'impressions toggle' do it 'optimized mode' do splits_imp_toggle = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_imp_toggle) factory_imp_toggle = SplitIoClient::SplitFactory.new('test_api_key', @@ -1236,9 +1238,10 @@ expect(imp_count.keys()[0].include? ('with_track_disabled')).to eq(true) expect(imp_count.length).to eq(1) end + it 'debug mode' do splits_imp_toggle = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_imp_toggle) factory_imp_toggle = SplitIoClient::SplitFactory.new('test_api_key', @@ -1272,7 +1275,7 @@ end it 'none mode' do splits_imp_toggle = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_imp_toggle) factory_imp_toggle = SplitIoClient::SplitFactory.new('test_api_key', @@ -1306,17 +1309,89 @@ expect(imp_count.length).to eq(3) end end + + context 'rule based segments' do + it 'using segment in excluded' do + splits_rbs = File.read(File.join(SplitIoClient.root, 'spec/test_data/rule_based_segments/rule_base_segments3.json')) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return(status: 200, body: splits_rbs) + stub_request(:get, "https://sdk.split.io/api/splitChanges?rbSince=1506703262916&s=1.3&since=1506703262916") + .to_return(status: 200, body: "") + stub_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=-1') + .to_return(status: 200, body: '{"name": "segment1","added": ["pato@split.io"],"removed": [],"since": -1,"till": 1470947453877}') + stub_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=1470947453877') + .to_return(status: 200, body: '{"name": "segment1","added": [],"removed": [],"since": 1470947453877,"till": 1470947453877}') + factory_rbs = + SplitIoClient::SplitFactory.new('test_api_key', + impressions_mode: :optimized, + features_refresh_rate: 9999, + telemetry_refresh_rate: 99999, + impressions_refresh_rate: 99999, + streaming_enabled: false) + + client_rbs = factory_rbs.client + client_rbs.block_until_ready + + expect(client_rbs.get_treatment('bilal@split.io', 'rbs_feature_flag', {:email => 'bilal@split.io'})).to eq('on') + expect(client_rbs.get_treatment('mauro@split.io', 'rbs_feature_flag', {:email => 'mauro@split.io'})).to eq('off') + expect(client_rbs.get_treatment('pato@split.io', 'rbs_feature_flag', {:email => 'pato@split.io'})).to eq('off') + end + + it 'using rb segment in excluded' do + splits_rbs = File.read(File.join(SplitIoClient.root, 'spec/test_data/rule_based_segments/rule_base_segments2.json')) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return(status: 200, body: splits_rbs) + stub_request(:get, "https://sdk.split.io/api/splitChanges?rbSince=1506703262916&s=1.3&since=1506703262916") + .to_return(status: 200, body: "") + factory_rbs = + SplitIoClient::SplitFactory.new('test_api_key', + impressions_mode: :optimized, + features_refresh_rate: 9999, + telemetry_refresh_rate: 99999, + impressions_refresh_rate: 99999, + streaming_enabled: false) + + client_rbs = factory_rbs.client + client_rbs.block_until_ready + + expect(client_rbs.get_treatment('bilal@split.io', 'rbs_feature_flag', {:email => 'bilal@split.io'})).to eq('off') + expect(client_rbs.get_treatment('mauro@split.io', 'rbs_feature_flag', {:email => 'mauro@split.io'})).to eq('off') + expect(client_rbs.get_treatment('bilal', 'rbs_feature_flag', {:email => 'bilal'})).to eq('on') + expect(client_rbs.get_treatment('bilal2@split.io', 'rbs_feature_flag', {:email => 'bilal2split.io'})).to eq('on') + end + + it 'using rb segment in condition' do + splits_rbs = File.read(File.join(SplitIoClient.root, 'spec/test_data/rule_based_segments/rule_base_segments.json')) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return(status: 200, body: splits_rbs) + stub_request(:get, "https://sdk.split.io/api/splitChanges?rbSince=1506703262916&s=1.3&since=1506703262916") + .to_return(status: 200, body: "") + factory_rbs = + SplitIoClient::SplitFactory.new('test_api_key', + impressions_mode: :optimized, + features_refresh_rate: 9999, + telemetry_refresh_rate: 99999, + impressions_refresh_rate: 99999, + streaming_enabled: false) + + client_rbs = factory_rbs.client + client_rbs.block_until_ready + + expect(client_rbs.get_treatment('bilal@split.io', 'rbs_feature_flag', {:email => 'bilal@split.io'})).to eq('on') + expect(client_rbs.get_treatment('mauro@split.io', 'rbs_feature_flag', {:email => 'mauro@split.io'})).to eq('off') + end + end end private def mock_split_changes(splits_json) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_json) end def mock_split_changes_error - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 500) end diff --git a/spec/integrations/push_client_spec.rb b/spec/integrations/push_client_spec.rb index 721340b2..f62d6d60 100644 --- a/spec/integrations/push_client_spec.rb +++ b/spec/integrations/push_client_spec.rb @@ -51,14 +51,14 @@ mock_splits_request(splits2, '1585948850109') mock_splits_request(splits3, '1585948850110') mock_segment_changes('segment3', segment3, '-1') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850111').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850111&rbSince=-1').to_return(status: 200, body: '') mock_server do |server| server.setup_response('/') do |_, res| send_content(res, event_split_update_must_fetch) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -74,9 +74,9 @@ client.block_until_ready sleep(2) expect(client.get_treatment('admin', 'push_test')).to eq('after_fetch') - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.at_least_times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850109')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850110')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.at_least_times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850109&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850110&rbSince=-1')).to have_been_made.times(1) client.destroy end end @@ -91,7 +91,7 @@ send_content(res, event_split_update_must_not_fetch) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -105,9 +105,9 @@ client.block_until_ready(1) sleep(1) expect(client.get_treatment('admin', 'push_test')).to eq('on') - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850109')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850110')).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850109&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850110&rbSince=-1')).to have_been_made.times(0) client.destroy end end @@ -123,7 +123,7 @@ send_content(res, event_split_update_missing_change_number) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -137,9 +137,9 @@ client.block_until_ready(1) sleep(1) expect(client.get_treatment('admin', 'push_test')).to eq('on') - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850109')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850110')).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850109&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850110&rbSince=-1')).to have_been_made.times(0) end end @@ -149,9 +149,9 @@ mock_splits_request(splits3, '1585948850110') mock_segment_changes('segment3', segment3, '-1') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850111").to_return(status: 200, body: '') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850111&rbSince=-1").to_return(status: 200, body: '') stub_request(:get, "https://sdk.split.io/api/segmentChanges/bilal_segment?since=-1").to_return(status: 200, body: '') - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) mock_server do |server| @@ -186,13 +186,13 @@ mock_splits_request(splits3, '1585948850110') mock_segment_changes('segment3', segment3, '-1') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850111').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850111&rbSince=-1').to_return(status: 200, body: '') mock_server do |server| server.setup_response('/') do |_, res| send_content(res, event_split_iff_update_incorrect_pcn) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -215,13 +215,13 @@ mock_splits_request(splits3, '1585948850110') mock_segment_changes('segment3', segment3, '-1') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850111').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850111&rbSince=-1').to_return(status: 200, body: '') mock_server do |server| server.setup_response('/') do |_, res| send_content(res, event_split_iff_update_missing_definition) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -244,13 +244,13 @@ mock_splits_request(splits3, '1585948850110') mock_segment_changes('segment3', segment3, '-1') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850111').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850111&rbSince=-1').to_return(status: 200, body: '') mock_server do |server| server.setup_response('/') do |_, res| send_content(res, event_split_iff_update_incorrect_compression) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -280,7 +280,7 @@ send_content(res, event_split_kill_must_fetch) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -293,9 +293,9 @@ client.block_until_ready sleep(2) expect(client.get_treatment('admin', 'push_test')).to eq('after_fetch') - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850109')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850110')).to have_been_made.at_least_times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850109&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850110&rbSince=-1')).to have_been_made.at_least_times(1) client.destroy end end @@ -304,14 +304,14 @@ mock_splits_request(splits, -1) mock_splits_request(splits2, 1_585_948_850_109) mock_segment_changes('segment3', segment3, '-1') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850110').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850110&rbSince=-1').to_return(status: 200, body: '') mock_server do |server| server.setup_response('/') do |_, res| send_content(res, event_split_kill_must_not_fetch) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -325,9 +325,9 @@ client.block_until_ready(1) sleep(2) expect(client.get_treatment('admin', 'push_test')).to eq('on') - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850109')).to have_been_made.times(1) - expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850110')).to have_been_made.times(0) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850109&rbSince=-1')).to have_been_made.times(1) + expect(a_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850110&rbSince=-1')).to have_been_made.times(0) client.destroy end end @@ -346,7 +346,7 @@ send_content(res, event_segment_update_must_fetch) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -375,7 +375,7 @@ send_content(res, event_segment_update_must_not_fetch) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -400,14 +400,14 @@ mock_splits_request(splits2, 1_585_948_850_109) mock_splits_request(splits3, 1_585_948_850_110) mock_segment_changes('segment3', segment3, '-1') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850111').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850111&rbSince=-1').to_return(status: 200, body: '') mock_server do |server| server.setup_response('/') do |_, res| send_content(res, event_occupancy_with_publishers) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -436,7 +436,7 @@ send_content(res, event_occupancy_without_publishers) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -467,7 +467,7 @@ send_content(res, event_control_STREAMING_PAUSED) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -496,7 +496,7 @@ send_content(res, event_control_STREAMING_RESUMED) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -519,14 +519,14 @@ mock_splits_request(splits2, 1_585_948_850_109) mock_splits_request(splits3, 1_585_948_850_110) mock_segment_changes('segment3', segment3, '-1') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1585948850111').to_return(status: 200, body: '') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1585948850111&rbSince=-1').to_return(status: 200, body: '') mock_server do |server| server.setup_response('/') do |_, res| send_content(res, event_control_STREAMING_DISABLED) end - stub_request(:get, auth_service_url + "?s=1.1").to_return(status: 200, body: auth_body_response) + stub_request(:get, auth_service_url + "?s=1.3").to_return(status: 200, body: auth_body_response) streaming_service_url = server.base_uri factory = SplitIoClient::SplitFactory.new( @@ -559,7 +559,7 @@ def send_content(res, content) end def mock_splits_request(splits_json, since) - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=#{since}") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=#{since}&rbSince=-1") .to_return(status: 200, body: splits_json) end diff --git a/spec/integrations/redis_client_spec.rb b/spec/integrations/redis_client_spec.rb index b19733db..e23c1525 100644 --- a/spec/integrations/redis_client_spec.rb +++ b/spec/integrations/redis_client_spec.rb @@ -997,7 +997,7 @@ private def load_splits_redis(splits_json, cli) - splits = JSON.parse(splits_json, symbolize_names: true)[:splits] + splits = JSON.parse(splits_json, symbolize_names: true)[:ff][:d] splits_repository = cli.instance_variable_get(:@splits_repository) diff --git a/spec/splitclient/split_client_spec.rb b/spec/splitclient/split_client_spec.rb index d8d7123d..fd19dc0c 100644 --- a/spec/splitclient/split_client_spec.rb +++ b/spec/splitclient/split_client_spec.rb @@ -10,19 +10,20 @@ let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) } let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } let(:impressions_repository) {SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } let(:events_repository) { SplitIoClient::Cache::Repositories::EventsRepository.new(config, 'sdk_key', runtime_producer) } let(:impression_manager) { SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, SplitIoClient::Engine::Common::NoopImpressionCounter.new, runtime_producer, SplitIoClient::Observers::NoopImpressionObserver.new, SplitIoClient::Engine::Impressions::NoopUniqueKeysTracker.new) } let(:evaluation_producer) { SplitIoClient::Telemetry::EvaluationProducer.new(config) } - let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, config) } - let(:split_client) { SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) } + let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) } + let(:split_client) { SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository, :rule_based_segments => rule_based_segments_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) } let(:splits) do File.read(File.join(SplitIoClient.root, 'spec/test_data/integrations/splits.json')) end before do - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][2]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][2]], [], -1) end it 'check getting treatments' do @@ -56,14 +57,14 @@ end it 'posting impressions and events' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits) stub_request(:post, 'https://events.split.io/api/events/bulk').to_return(status: 200, body: '') stub_request(:post, 'https://events.split.io/api/testImpressions/bulk').to_return(status: 200, body: '') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: '') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/usage').to_return(status: 200, body: '') - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916").to_return(status: 200, body: 'ok') - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1506703262916sets=set_3&").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1&sets=set_3&").to_return(status: 200, body: 'ok') mock_segment_changes('segment1', segment1, '-1') mock_segment_changes('segment1', segment1, '1470947453877') mock_segment_changes('segment2', segment2, '-1') @@ -100,6 +101,7 @@ flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) splits_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) impressions_repository = SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) + rule_based_segments_repository = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) runtime_producer = SplitIoClient::Telemetry::RuntimeProducer.new(config) events_repository = SplitIoClient::Cache::Repositories::EventsRepository.new(config, 'sdk_key', runtime_producer) impressions_counter = SplitIoClient::Engine::Common::ImpressionCounter.new @@ -107,13 +109,13 @@ unique_keys_tracker = SplitIoClient::Engine::Impressions::UniqueKeysTracker.new(config, filter_adapter, nil, Concurrent::Hash.new) impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) - evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, config) + evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) splits = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][0]], [], -1) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][1]], [], -1) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][2]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][0]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][1]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][2]], [], -1) expect(split_client.get_treatment('key1', 'with_track_disabled')).to eq('off') expect(split_client.get_treatment('key2', 'with_track_enabled')).to eq('off') @@ -139,6 +141,7 @@ flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) splits_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) impressions_repository = SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) + rule_based_segments_repository = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) runtime_producer = SplitIoClient::Telemetry::RuntimeProducer.new(config) events_repository = SplitIoClient::Cache::Repositories::EventsRepository.new(config, 'sdk_key', runtime_producer) impressions_counter = SplitIoClient::Engine::Common::ImpressionCounter.new @@ -146,13 +149,13 @@ unique_keys_tracker = SplitIoClient::Engine::Impressions::UniqueKeysTracker.new(config, filter_adapter, nil, Concurrent::Hash.new) impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) - evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, config) + evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) splits = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][0]], [], -1) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][1]], [], -1) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][2]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][0]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][1]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][2]], [], -1) expect(split_client.get_treatment('key1', 'with_track_disabled')).to eq('off') expect(split_client.get_treatment('key2', 'with_track_enabled')).to eq('off') @@ -178,6 +181,7 @@ flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) splits_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) impressions_repository = SplitIoClient::Cache::Repositories::ImpressionsRepository.new(config) + rule_based_segments_repository = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) runtime_producer = SplitIoClient::Telemetry::RuntimeProducer.new(config) events_repository = SplitIoClient::Cache::Repositories::EventsRepository.new(config, 'sdk_key', runtime_producer) impressions_counter = SplitIoClient::Engine::Common::ImpressionCounter.new @@ -185,13 +189,13 @@ unique_keys_tracker = SplitIoClient::Engine::Impressions::UniqueKeysTracker.new(config, filter_adapter, nil, Concurrent::Hash.new) impression_manager = SplitIoClient::Engine::Common::ImpressionManager.new(config, impressions_repository, impressions_counter, runtime_producer, SplitIoClient::Observers::ImpressionObserver.new, unique_keys_tracker) evaluation_producer = SplitIoClient::Telemetry::EvaluationProducer.new(config) - evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, config) + evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rule_based_segments_repository, config) split_client = SplitIoClient::SplitClient.new('sdk_key', {:splits => splits_repository, :segments => segments_repository, :impressions => impressions_repository, :events => events_repository}, nil, config, impression_manager, evaluation_producer, evaluator, SplitIoClient::Validators.new(config)) splits = File.read(File.join(SplitIoClient.root, 'spec/test_data/splits/imp-toggle.json')) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][0]], [], -1) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][1]], [], -1) - splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:splits][2]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][0]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][1]], [], -1) + splits_repository.update([JSON.parse(splits,:symbolize_names => true)[:ff][:d][2]], [], -1) expect(split_client.get_treatment('key1', 'with_track_disabled')).to eq('off') expect(split_client.get_treatment('key2', 'with_track_enabled')).to eq('off') diff --git a/spec/splitclient/split_factory_spec.rb b/spec/splitclient/split_factory_spec.rb index 5cfec28c..dd0ef73a 100644 --- a/spec/splitclient/split_factory_spec.rb +++ b/spec/splitclient/split_factory_spec.rb @@ -69,7 +69,7 @@ let(:mode) { :standalone } it 'log an error stating Api Key is invalid' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: '') @@ -89,7 +89,7 @@ let(:mode) { :standalone } it 'log an error stating Api Key is invalid' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: '') @@ -112,9 +112,9 @@ end it 'log an error stating Api Key is invalid' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits_with_segments_json) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1473863097220') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1473863097220&rbSince=-1') .to_return(status: 200, body: []) stub_request(:get, 'https://sdk.split.io/api/segmentChanges/employees?since=-1') .to_return(status: 403, body: []) @@ -140,7 +140,7 @@ let(:mode) { :standalone } it 'log an error' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/usage') @@ -174,7 +174,7 @@ end it 'logs warnings stating number of factories' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) described_class.new('API_KEY', options) @@ -188,7 +188,7 @@ end it 'decreases number of registered factories on client destroy' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) expect(SplitIoClient.split_factory_registry.number_of_factories_for('API_KEY')).to eq 0 @@ -203,7 +203,7 @@ end it 'active and redundant factories' do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') @@ -239,7 +239,7 @@ .to_return(status: 200, body: 'ok') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: 'ok') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: '') @@ -260,7 +260,7 @@ .to_return(status: 200, body: 'ok') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: 'ok') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: '') @@ -281,7 +281,7 @@ .to_return(status: 200, body: 'ok') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: 'ok') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: '') @@ -302,7 +302,7 @@ .to_return(status: 200, body: 'ok') stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: 'ok') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: []) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: '') diff --git a/spec/splitclient/split_manager_spec.rb b/spec/splitclient/split_manager_spec.rb index 4714a40f..0f9bdd0e 100644 --- a/spec/splitclient/split_manager_spec.rb +++ b/spec/splitclient/split_manager_spec.rb @@ -10,7 +10,7 @@ let(:segments) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../test_data/segments/engine_segments.json'))) } before do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits) stub_request(:get, 'https://sdk.split.io/api/segmentChanges/demo?since=-1') @@ -25,7 +25,7 @@ stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config') .to_return(status: 200, body: 'ok') - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1473413807667") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1473413807667&rbSince=-1") .to_return(status: 200, body: "", headers: {}) end @@ -85,7 +85,7 @@ context '#splits' do it 'returns empty array and logs error when not ready' do - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=-1") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=-1") .to_return(status: 200, body: "", headers: {}) allow(subject).to receive(:ready?).and_return(false) @@ -109,10 +109,10 @@ let(:splits3) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../test_data/splits/splits3.json'))) } before do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits3) - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since=1473863097220") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1473863097220&rbSince=-1") .to_return(status: 200, body: "", headers: {}) end @@ -137,10 +137,10 @@ let(:splits4) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../test_data/splits/splits4.json'))) } before do - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1') + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits4) - stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.1&since") + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since") .to_return(status: 200, body: "", headers: {}) end diff --git a/spec/splitclient_rb_corner_cases_spec.rb b/spec/splitclient_rb_corner_cases_spec.rb index b5d763ea..c4346f85 100644 --- a/spec/splitclient_rb_corner_cases_spec.rb +++ b/spec/splitclient_rb_corner_cases_spec.rb @@ -21,10 +21,10 @@ before do stub_request(:post, 'https://events.split.io/api/testImpressions/bulk').to_return(status: 200, body: '') - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=-1').to_return(status: 200, body: splits_json) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1').to_return(status: 200, body: splits_json) stub_request(:get, 'https://sdk.split.io/api/segmentChanges/demo?since=-1').to_return(status: 200, body: segment_res) stub_request(:get, 'https://sdk.split.io/api/segmentChanges/employees?since=-1').to_return(status: 200, body: segment_res) - stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.1&since=1473413807667').to_return(status: 200, body: segment_res) + stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=1473413807667&rbSince=-1').to_return(status: 200, body: segment_res) stub_request(:post, 'https://telemetry.split.io/api/v1/metrics/config').to_return(status: 200, body: segment_res) stub_request(:post, 'https://events.split.io/api/testImpressions/count').to_return(status: 200, body: '') end diff --git a/spec/test_data/rule_based_segments/rule_base_segments.json b/spec/test_data/rule_based_segments/rule_base_segments.json new file mode 100644 index 00000000..deafbdc2 --- /dev/null +++ b/spec/test_data/rule_based_segments/rule_base_segments.json @@ -0,0 +1,135 @@ +{"ff": {"d": [ + { + "changeNumber": 10, + "trafficTypeName": "user", + "name": "rbs_feature_flag", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "in rule based segment sample_rule_based_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + } + ], "t": 1506703262916, "s": -1}, +"rbs": {"t": 1506703262916, "s": -1, "d": + [{ + "changeNumber": 5, + "name": "dependent_rbs", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{"keys":["mauro@split.io","gaston@split.io"],"segments":[]}, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ]}, + { + "changeNumber": 5, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [], + "segments": [] + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "dependent_rbs" + } + } + ] + } + } + ] + }] +}} diff --git a/spec/test_data/rule_based_segments/rule_base_segments2.json b/spec/test_data/rule_based_segments/rule_base_segments2.json new file mode 100644 index 00000000..f0ab0e1e --- /dev/null +++ b/spec/test_data/rule_based_segments/rule_base_segments2.json @@ -0,0 +1,140 @@ +{"ff": {"d": [ + { + "changeNumber": 10, + "trafficTypeName": "user", + "name": "rbs_feature_flag", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "in rule based segment sample_rule_based_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + } +], "t": 1506703262916, "s": -1}, +"rbs": {"t": 1506703262916, "s": -1, "d": [ + { + "changeNumber": 5, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{ + "keys":["mauro@split.io","gaston@split.io"], + "segments":[{"type":"rule-based", "name":"no_excludes"}] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "START_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "bilal" + ] + } + } + ] + } + } + ] + }, + { + "changeNumber": 5, + "name": "no_excludes", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{ + "keys":["bilal2@split.io"], + "segments":[] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + } +]}} diff --git a/spec/test_data/rule_based_segments/rule_base_segments3.json b/spec/test_data/rule_based_segments/rule_base_segments3.json new file mode 100644 index 00000000..fa7af16f --- /dev/null +++ b/spec/test_data/rule_based_segments/rule_base_segments3.json @@ -0,0 +1,108 @@ +{"ff": {"d": [ + { + "changeNumber": 10, + "trafficTypeName": "user", + "name": "rbs_feature_flag", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "in rule based segment sample_rule_based_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + } +], "t": 1506703262916, "s": -1}, +"rbs": {"t": 1506703262916, "s": -1, "d": [ + { + "changeNumber": 5, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{ + "keys":["mauro@split.io","gaston@split.io"], + "segments":[{"type":"standard", "name":"segment1"}] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + } +]}} diff --git a/spec/test_data/rule_based_segments/split_old_spec.json b/spec/test_data/rule_based_segments/split_old_spec.json new file mode 100644 index 00000000..0d7edf86 --- /dev/null +++ b/spec/test_data/rule_based_segments/split_old_spec.json @@ -0,0 +1,328 @@ +{ + "splits": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "whitelist_feature", + "seed": -1222652054, + "status": "ACTIVE", + "killed": false, + "changeNumber": 123, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "whitelisted_user" + ] + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ] + }, + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ] + } + ], + "sets": ["set1", "set2"] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "all_feature", + "seed": 1699838640, + "status": "ACTIVE", + "killed": false, + "changeNumber": 123, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ] + } + ], + "sets": ["set4"] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "killed_feature", + "seed": -480091424, + "status": "ACTIVE", + "killed": true, + "changeNumber": 123, + "defaultTreatment": "defTreatment", + "configurations": { + "off": "{\"size\":15,\"test\":20}", + "defTreatment": "{\"size\":15,\"defTreatment\":true}" + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "defTreatment", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ] + } + ], + "sets": ["set3"] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "sample_feature", + "seed": 1548363147, + "status": "ACTIVE", + "killed": false, + "changeNumber": 123, + "defaultTreatment": "off", + "configurations": { + "on": "{\"size\":15,\"test\":20}" + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ] + }, + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "human_beigns" + }, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 30 + }, + { + "treatment": "off", + "size": 70 + } + ] + } + ], + "sets": ["set1"] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "dependency_test", + "seed": 1222652054, + "status": "ACTIVE", + "killed": false, + "changeNumber": 123, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "userDefinedSegmentMatcherData": null, + "dependencyMatcherData": { + "split": "all_feature", + "treatments": ["on"] + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ] + } + ], + "sets": [] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "regex_test", + "seed": 1222652051, + "status": "ACTIVE", + "killed": false, + "changeNumber": 123, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "stringMatcherData": "abc[0-9]" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ] + } + ], + "sets": [] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "boolean_test", + "status": "ACTIVE", + "killed": false, + "changeNumber": 123, + "seed": 12321809, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "EQUAL_TO_BOOLEAN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "booleanMatcherData": true + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ] + } + ], + "sets": [] + } + ], + "since": -1, + "till": 1457726098069 +} \ No newline at end of file diff --git a/spec/test_data/splits/between_matcher/datetime_matcher_splits.json b/spec/test_data/splits/between_matcher/datetime_matcher_splits.json index d1e3b3df..73e7ffe2 100644 --- a/spec/test_data/splits/between_matcher/datetime_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/datetime_matcher_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -50,5 +50,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json b/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json index 46d8bab1..9d93d964 100644 --- a/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -50,5 +50,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json b/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json index 64f4182f..105ca471 100644 --- a/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -50,5 +50,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/between_matcher/number_matcher_splits.json b/spec/test_data/splits/between_matcher/number_matcher_splits.json index bef6b87a..f5e98ff0 100644 --- a/spec/test_data/splits/between_matcher/number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/number_matcher_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -50,5 +50,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/boolean_matcher/splits.json b/spec/test_data/splits/boolean_matcher/splits.json index 6cfd69e7..9f81228e 100644 --- a/spec/test_data/splits/boolean_matcher/splits.json +++ b/spec/test_data/splits/boolean_matcher/splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -46,5 +46,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/combining_matcher_splits.json b/spec/test_data/splits/combining_matcher_splits.json index fc2ddcc1..86c258ea 100644 --- a/spec/test_data/splits/combining_matcher_splits.json +++ b/spec/test_data/splits/combining_matcher_splits.json @@ -1,5 +1,6 @@ +{ "ff":{ + "d": [ { - "splits": [{ "trafficTypeName": "user", "name": "PASSENGER_anding", "trafficAllocation": 100, @@ -111,5 +112,6 @@ }], "label": "in segment employees" }] - }] + }],"s":-1, "t":-1}, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/engine/all_keys_matcher.json b/spec/test_data/splits/engine/all_keys_matcher.json index 107e5e2d..70c425f8 100644 --- a/spec/test_data/splits/engine/all_keys_matcher.json +++ b/spec/test_data/splits/engine/all_keys_matcher.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -36,5 +36,8 @@ ], "sets": ["set_1"] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/configurations.json b/spec/test_data/splits/engine/configurations.json index 05ccef7a..edaecd8d 100644 --- a/spec/test_data/splits/engine/configurations.json +++ b/spec/test_data/splits/engine/configurations.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -172,5 +172,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/dependency_matcher.json b/spec/test_data/splits/engine/dependency_matcher.json index 5a85fa41..ba4f1c4d 100644 --- a/spec/test_data/splits/engine/dependency_matcher.json +++ b/spec/test_data/splits/engine/dependency_matcher.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -73,5 +73,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/equal_to_set_matcher.json b/spec/test_data/splits/engine/equal_to_set_matcher.json index b7c07f23..83f7cc6e 100644 --- a/spec/test_data/splits/engine/equal_to_set_matcher.json +++ b/spec/test_data/splits/engine/equal_to_set_matcher.json @@ -1,5 +1,5 @@ -{ - "splits":[ +{ "ff":{ + "d": [ { "trafficTypeName": "user", "name": "mauro_test", @@ -119,5 +119,8 @@ ], "sets": ["set_2"] } - ] -} \ No newline at end of file + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} +} diff --git a/spec/test_data/splits/engine/impressions_test.json b/spec/test_data/splits/engine/impressions_test.json index c4743e68..79c1524c 100644 --- a/spec/test_data/splits/engine/impressions_test.json +++ b/spec/test_data/splits/engine/impressions_test.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -85,5 +85,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/killed.json b/spec/test_data/splits/engine/killed.json index 2a0315cf..be6f02ba 100644 --- a/spec/test_data/splits/engine/killed.json +++ b/spec/test_data/splits/engine/killed.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -32,5 +32,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/segment_deleted_matcher.json b/spec/test_data/splits/engine/segment_deleted_matcher.json index a558e5f0..e7816f47 100644 --- a/spec/test_data/splits/engine/segment_deleted_matcher.json +++ b/spec/test_data/splits/engine/segment_deleted_matcher.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -38,5 +38,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/segment_matcher.json b/spec/test_data/splits/engine/segment_matcher.json index 079049d2..9c7980f1 100644 --- a/spec/test_data/splits/engine/segment_matcher.json +++ b/spec/test_data/splits/engine/segment_matcher.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -39,5 +39,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/segment_matcher2.json b/spec/test_data/splits/engine/segment_matcher2.json index 62737bbe..d5e25dfc 100644 --- a/spec/test_data/splits/engine/segment_matcher2.json +++ b/spec/test_data/splits/engine/segment_matcher2.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -117,5 +117,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/engine/whitelist_matcher.json b/spec/test_data/splits/engine/whitelist_matcher.json index c9e756ad..1c491172 100644 --- a/spec/test_data/splits/engine/whitelist_matcher.json +++ b/spec/test_data/splits/engine/whitelist_matcher.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -37,5 +37,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/equal_to_matcher/date_splits.json b/spec/test_data/splits/equal_to_matcher/date_splits.json index fa5fa89b..5267517b 100644 --- a/spec/test_data/splits/equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/equal_to_matcher/date_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -46,5 +46,8 @@ ], "sets": ["set_1"] } - ] + ], + "s": -1, + "t": -1}, + "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/equal_to_matcher/negative_splits.json b/spec/test_data/splits/equal_to_matcher/negative_splits.json index 84f5fc33..548bcee8 100644 --- a/spec/test_data/splits/equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/equal_to_matcher/negative_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -46,5 +46,8 @@ ], "sets": ["set_1"] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/equal_to_matcher/splits.json b/spec/test_data/splits/equal_to_matcher/splits.json index 13f2cae6..5f603999 100644 --- a/spec/test_data/splits/equal_to_matcher/splits.json +++ b/spec/test_data/splits/equal_to_matcher/splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -46,5 +46,8 @@ ], "sets": ["set_1"] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/equal_to_matcher/zero_splits.json b/spec/test_data/splits/equal_to_matcher/zero_splits.json index 17c63578..da231d90 100644 --- a/spec/test_data/splits/equal_to_matcher/zero_splits.json +++ b/spec/test_data/splits/equal_to_matcher/zero_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -46,5 +46,8 @@ ], "sets": ["set_1"] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json index 59f29335..643e07a9 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -45,5 +45,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json index 755948b3..0d5ccde7 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -45,5 +45,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json index e8a7f72f..730aa286 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -45,5 +45,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/imp-toggle.json b/spec/test_data/splits/imp-toggle.json index 77d7a4e9..b70d6684 100644 --- a/spec/test_data/splits/imp-toggle.json +++ b/spec/test_data/splits/imp-toggle.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "trafficTypeName": "user", "name": "with_track_disabled", @@ -152,4 +152,6 @@ ], "since": -1, "till": 1675259356568 + }, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json index 6faf61c3..0f74fa5b 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -45,5 +45,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json index 7630b564..eab2e7e2 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":null, "environment":null, @@ -49,5 +49,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json index ca725e3c..db1a51f5 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -45,5 +45,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json index 76850210..cc2e6dc3 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -45,5 +45,8 @@ } ] } - ] + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} } diff --git a/spec/test_data/splits/semver_matchers/semver_between.json b/spec/test_data/splits/semver_matchers/semver_between.json index 44edc2b6..9a0a3c7b 100644 --- a/spec/test_data/splits/semver_matchers/semver_between.json +++ b/spec/test_data/splits/semver_matchers/semver_between.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "trafficTypeName": "user", "name": "semver_between", @@ -82,5 +82,6 @@ } ], "since": -1, - "till": 1675259356568 + "till": 1675259356568}, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/semver_matchers/semver_equalto.json b/spec/test_data/splits/semver_matchers/semver_equalto.json index c3daa9ea..e7699e16 100644 --- a/spec/test_data/splits/semver_matchers/semver_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_equalto.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "trafficTypeName": "user", "name": "semver_equalto", @@ -81,5 +81,6 @@ } ], "since": -1, - "till": 1675259356568 + "till": 1675259356568}, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json index 40f0f036..1fde9c28 100644 --- a/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "trafficTypeName": "user", "name": "semver_greater_or_equalto", @@ -81,5 +81,6 @@ } ], "since": -1, - "till": 1675259356568 + "till": 1675259356568}, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/semver_matchers/semver_inlist.json b/spec/test_data/splits/semver_matchers/semver_inlist.json index 9f1e6246..8feafc58 100644 --- a/spec/test_data/splits/semver_matchers/semver_inlist.json +++ b/spec/test_data/splits/semver_matchers/semver_inlist.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "trafficTypeName": "user", "name": "semver_inlist", @@ -82,5 +82,6 @@ } ], "since": -1, - "till": 1675259356568 + "till": 1675259356568}, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json index 9a46807f..6704a6c3 100644 --- a/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "trafficTypeName": "user", "name": "semver_less_or_equalto", @@ -81,5 +81,6 @@ } ], "since": -1, - "till": 1675259356568 + "till": 1675259356568}, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/splits4.json b/spec/test_data/splits/splits4.json index 17f0ec88..f9d3f993 100644 --- a/spec/test_data/splits/splits4.json +++ b/spec/test_data/splits/splits4.json @@ -1,5 +1,6 @@ -{ - "splits": [{ +{"ff": { + "d":[ + { "trafficTypeName": "user", "name": "uber_feature", "trafficAllocation": 100, @@ -83,4 +84,6 @@ "label": "category in list [expert] and last_ride on or after 2017-07-27 01:18AM" }] } -]} +] +}, "rbs": {"d":[], "s":-1, "t":-1} +} diff --git a/spec/test_data/splits/splits_traffic_allocation.json b/spec/test_data/splits/splits_traffic_allocation.json index ce35c5e9..e1a58647 100644 --- a/spec/test_data/splits/splits_traffic_allocation.json +++ b/spec/test_data/splits/splits_traffic_allocation.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI", @@ -355,4 +355,5 @@ ], "since": -1, "till": 1470855828956 +}, "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/splits_traffic_allocation_one_percent.json b/spec/test_data/splits/splits_traffic_allocation_one_percent.json index 5b523c21..d480a521 100644 --- a/spec/test_data/splits/splits_traffic_allocation_one_percent.json +++ b/spec/test_data/splits/splits_traffic_allocation_one_percent.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "name": "Traffic_Allocation_One_Percent", "algo": 1, @@ -37,5 +37,6 @@ } ] } - ] + ], "s":-1, "t":-1}, + "rbs": {"d":[], "s":-1, "t":-1} } diff --git a/spec/test_data/splits/whitelist_matcher_splits.json b/spec/test_data/splits/whitelist_matcher_splits.json index 1a517656..81259de9 100644 --- a/spec/test_data/splits/whitelist_matcher_splits.json +++ b/spec/test_data/splits/whitelist_matcher_splits.json @@ -1,5 +1,5 @@ -{ - "splits": [ +{ "ff":{ + "d": [ { "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", @@ -52,5 +52,6 @@ } ] } - ] + ], "s":-1, "t":-1}, + "rbs": {"d":[], "s":-1, "t":-1} } From 61e502f817c62e113dfc541380569819780d4dc8 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 14 May 2025 21:12:00 -0700 Subject: [PATCH 19/44] Added old spec support --- .../cache/fetchers/split_fetcher.rb | 4 +- lib/splitclient-rb/engine/api/client.rb | 3 + lib/splitclient-rb/engine/api/splits.rb | 59 +++++++++++++++-- .../helpers/repository_helper.rb | 7 +- lib/splitclient-rb/split_config.rb | 4 ++ .../sse/workers/splits_worker.rb | 4 +- spec/engine/api/splits_spec.rb | 51 ++++++++++++++- spec/integrations/old_spec_client_spec.rb | 42 ++++++++++++ .../rule_based_segments/split_old_spec.json | 64 ------------------- 9 files changed, 163 insertions(+), 75 deletions(-) create mode 100644 spec/integrations/old_spec_client_spec.rb diff --git a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb index c2772d62..cfa0b636 100644 --- a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +++ b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb @@ -25,8 +25,8 @@ def call def fetch_splits(fetch_options = { cache_control_headers: false, till: nil }) @semaphore.synchronize do data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config) - SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config, @splits_api.clear_storage) + SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config, @splits_api.clear_storage) @splits_repository.set_segment_names(data[:segment_names]) @rule_based_segments_repository.set_segment_names(data[:segment_names]) @config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled diff --git a/lib/splitclient-rb/engine/api/client.rb b/lib/splitclient-rb/engine/api/client.rb index 65dcd027..d9d6e0df 100644 --- a/lib/splitclient-rb/engine/api/client.rb +++ b/lib/splitclient-rb/engine/api/client.rb @@ -50,6 +50,9 @@ def post_api(url, api_key, data, headers = {}, params = {}) raise e, 'Split SDK failed to connect to backend to post information', e.backtrace end + def sdk_url_overriden? + @config.sdk_url_overriden? + end private def api_client diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index 0596c9de..45c1b30f 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -5,28 +5,68 @@ module Api # Retrieves split definitions from the Split Backend class Splits < Client + PROXY_CHECK_INTERVAL_SECONDS = 24 * 60 * 60 + SPEC_1_1 = "1.1" + def initialize(api_key, config, telemetry_runtime_producer) super(config) @api_key = api_key @telemetry_runtime_producer = telemetry_runtime_producer @flag_sets_filter = @config.flag_sets_filter + @spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION + @last_proxy_check_timestamp = 0 + @clear_storage = false end def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil}) start = Time.now + + if check_last_proxy_check_timestamp + puts "switching to new spec" + @spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION + @config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.") + since = -1 + since_rbs = -1 + fetch_options = { cache_control_headers: false, till: nil, sets: nil} + end + + if @spec_version == Splits::SPEC_1_1 + params = { s: @spec_version, since: since } + else + params = { s: @spec_version, since: since, rbSince: since_rbs } + end - params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs } params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty? params[:till] = fetch_options[:till] unless fetch_options[:till].nil? @config.logger.debug("Fetching from splitChanges with #{params}: ") response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers]) + if response.status == 414 @config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.") raise ApiException.new response.body, 414 end + + if response.status == 400 and sdk_url_overriden? and @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION + @config.logger.warn("Detected proxy response error, changing spec version from #{@spec_version} to #{Splits::SPEC_1_1} and re-fetching.") + @spec_version = Splits::SPEC_1_1 + @last_proxy_check_timestamp = Time.now + return since(since, 0, fetch_options = {cache_control_headers: fetch_options[:cache_control_headers], till: fetch_options[:till], + sets: fetch_options[:sets]}) + end + if response.success? - result = objects_with_segment_names(response.body) + result = JSON.parse(response.body, symbolize_names: true) + if @spec_version == Splits::SPEC_1_1 + result = convert_to_newSPEC(result) + end + + result = objects_with_segment_names(result) + if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION + @clear_storage = @last_proxy_check_timestamp != 0 + @last_proxy_check_timestamp = 0 + end + unless result[:ff][:d].empty? @config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}") end @@ -52,10 +92,13 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till end end + def clear_storage + @clear_storage + end + private - def objects_with_segment_names(objects_json) - parsed_objects = JSON.parse(objects_json, symbolize_names: true) + def objects_with_segment_names(parsed_objects) parsed_objects[:segment_names] = Set.new parsed_objects[:segment_names] = parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits| @@ -76,6 +119,14 @@ def objects_with_segment_names(objects_json) parsed_objects end + + def check_last_proxy_check_timestamp + @spec_version == Splits::SPEC_1_1 and ((Time.now - @last_proxy_check_timestamp) >= Splits::PROXY_CHECK_INTERVAL_SECONDS) + end + + def convert_to_newSPEC(body) + {:ff => {:d => body[:splits], :s => body[:since], :t => body[:till]}, :rbs => {:d => [], :s => -1, :t => -1}} + end end end end diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index 24fdd76b..be0e63c8 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -3,7 +3,7 @@ module SplitIoClient module Helpers class RepositoryHelper - def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config) + def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config, clear_storage) to_add = [] to_delete = [] feature_flags.each do |feature_flag| @@ -23,10 +23,11 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled to_add.push(feature_flag) end + feature_flag_repository.clear if clear_storage feature_flag_repository.update(to_add, to_delete, change_number) end - def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config) + def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config, clear_storage) to_add = [] to_delete = [] rule_based_segments.each do |rule_based_segment| @@ -39,6 +40,8 @@ def self.update_rule_based_segment_repository(rule_based_segment_repository, rul config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled to_add.push(rule_based_segment) end + rule_based_segment_repository.clear if clear_storage + rule_based_segment_repository.update(to_add, to_delete, change_number) end end diff --git a/lib/splitclient-rb/split_config.rb b/lib/splitclient-rb/split_config.rb index da434c91..de7f3e4d 100644 --- a/lib/splitclient-rb/split_config.rb +++ b/lib/splitclient-rb/split_config.rb @@ -645,6 +645,10 @@ def consumer? @mode.equal?(:consumer) end + def sdk_url_overriden? + return @base_uri != SplitConfig.default_base_uri + end + # # gets the hostname where the sdk gem is running # diff --git a/lib/splitclient-rb/sse/workers/splits_worker.rb b/lib/splitclient-rb/sse/workers/splits_worker.rb index 419a3814..88b06660 100644 --- a/lib/splitclient-rb/sse/workers/splits_worker.rb +++ b/lib/splitclient-rb/sse/workers/splits_worker.rb @@ -71,7 +71,7 @@ def update_feature_flag(notification) new_split = return_object_from_json(notification) SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository, [new_split], - notification.data['changeNumber'], @config) + notification.data['changeNumber'], @config, false) fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_SEGMENT"), @feature_flags_repository) if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_RULE_BASED_SEGMENT"), notification.data['changeNumber']) return true @@ -93,7 +93,7 @@ def update_rule_based_segment(notification) new_rb_segment = return_object_from_json(notification) SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository, [new_rb_segment], - notification.data['changeNumber'], @config) + notification.data['changeNumber'], @config, false) fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_rb_segment, "IN_SEGMENT"), @rule_based_segment_repository) # TODO: enable when telemetry spec is added diff --git a/spec/engine/api/splits_spec.rb b/spec/engine/api/splits_spec.rb index af20bd9a..2d293498 100644 --- a/spec/engine/api/splits_spec.rb +++ b/spec/engine/api/splits_spec.rb @@ -21,7 +21,7 @@ stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1') .to_return(status: 200, body: splits) - parsed_splits = splits_api.send(:objects_with_segment_names, splits) + parsed_splits = splits_api.send(:objects_with_segment_names, JSON.parse(splits, symbolize_names: true)) expect(parsed_splits[:segment_names]).to eq(Set.new(%w[demo employees])) end @@ -183,4 +183,53 @@ ) end end + + context 'old spec tests' do + let(:old_spec_splits) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../../test_data/rule_based_segments/split_old_spec.json'))) } + let(:config) do + SplitIoClient::SplitConfig.new( + logger: Logger.new(log), + debug_enabled: true, + transport_debug_enabled: true, + base_uri: "https://proxy-server/api" + ) + end + let(:log) { StringIO.new } + let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) } + let(:splits_api) { described_class.new('', config, telemetry_runtime_producer) } + + it 'switch to old spec url whith proper conditions' do + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return(status: 400, body: '') + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1') + .to_return(status: 200, body: old_spec_splits) + + parsed_splits = splits_api.since(-1, -1) + + expect(parsed_splits[:ff][:d].length()).to eq(7) + expect(parsed_splits[:ff][:t]).to eq(1457726098069) + expect(parsed_splits[:ff][:s]).to eq(-1) + expect(parsed_splits[:rbs]).to eq({:d => [], :s => -1, :t => -1}) + expect(splits_api.clear_storage).to eq(false) + end + + it 'check new spec after last proxy timestamp expires' do + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return({status: 400, body: ''}, {status: 200, body: splits}) + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1') + .to_return(status: 200, body: old_spec_splits) + + parsed_splits = splits_api.since(-1, -1) + expect(parsed_splits[:ff][:d].length()).to eq(7) + expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Api::Splits::SPEC_1_1) + + SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1 + sleep 1 + parsed_splits = splits_api.since(-1, -1) + expect(splits_api.clear_storage).to eq(true) + expect(parsed_splits[:ff][:d].length()).to eq(2) + expect(parsed_splits[:rbs][:d].length()).to eq(1) + expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Spec::FeatureFlags::SPEC_VERSION) + end + end end diff --git a/spec/integrations/old_spec_client_spec.rb b/spec/integrations/old_spec_client_spec.rb new file mode 100644 index 00000000..7ee2526c --- /dev/null +++ b/spec/integrations/old_spec_client_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient do + context 'old spec tests' do + let(:old_spec_splits) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../test_data/rule_based_segments/split_old_spec.json'))) } + + it 'check new spec after last proxy timestamp expires' do + splits_rbs = File.read(File.join(SplitIoClient.root, 'spec/test_data/rule_based_segments/rule_base_segments.json')) + + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return({status: 400, body: ''}, {status: 200, body: splits_rbs}) + stub_request(:get, "https://sdk.split.io/api/splitChanges?rbSince=1506703262916&s=1.3&since=1506703262916") + .to_return(status: 200, body: '') + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1') + .to_return(status: 200, body: old_spec_splits) + stub_request(:get, "https://proxy-server/api/splitChanges?s=1.1&since=1457726098069") + .to_return(status: 200, body: '') + stub_request(:post, "https://telemetry.split.io/api/v1/metrics/config") + .to_return(status: 200, body: '') + + factory_old_spec = + SplitIoClient::SplitFactory.new('test_api_key', + {impressions_mode: :none, + features_refresh_rate: 2, + base_uri: "https://proxy-server/api", + streaming_enabled: false}) + + SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1 + client_old_spec = factory_old_spec.client + client_old_spec.block_until_ready + expect(client_old_spec.get_treatment('whitelisted_user', 'whitelist_feature')).to eq('on') + + sleep 1 + split_fetcher = factory_old_spec.instance_variable_get(:@split_fetcher) + split_fetcher.fetch_splits + sleep 1 + expect(client_old_spec.get_treatment('bilal@split.io', 'rbs_feature_flag', {:email => 'bilal@split.io'})).to eq('on') + end + end +end diff --git a/spec/test_data/rule_based_segments/split_old_spec.json b/spec/test_data/rule_based_segments/split_old_spec.json index 0d7edf86..2d0aef1e 100644 --- a/spec/test_data/rule_based_segments/split_old_spec.json +++ b/spec/test_data/rule_based_segments/split_old_spec.json @@ -141,70 +141,6 @@ ], "sets": ["set3"] }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "sample_feature", - "seed": 1548363147, - "status": "ACTIVE", - "killed": false, - "changeNumber": 123, - "defaultTreatment": "off", - "configurations": { - "on": "{\"size\":15,\"test\":20}" - }, - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" - }, - "whitelistMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "human_beigns" - }, - "whitelistMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 30 - }, - { - "treatment": "off", - "size": 70 - } - ] - } - ], - "sets": ["set1"] - }, { "orgId": null, "environment": null, From 2e54131fd0abcad871d1fc7fdf1e9e1f7663a6d9 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 15 May 2025 09:35:03 -0700 Subject: [PATCH 20/44] update tests --- .../cache/fetchers/split_fetcher.rb | 2 +- .../cache/repositories/splits_repository.rb | 4 ++ lib/splitclient-rb/engine/api/splits.rb | 1 - .../helpers/repository_helper.rb | 3 +- .../sse/workers/splits_worker.rb | 2 +- spec/integrations/in_memory_client_spec.rb | 38 +++++++++++++++++ spec/integrations/old_spec_client_spec.rb | 42 ------------------- 7 files changed, 45 insertions(+), 47 deletions(-) delete mode 100644 spec/integrations/old_spec_client_spec.rb diff --git a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb index cfa0b636..c1cba9dc 100644 --- a/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +++ b/lib/splitclient-rb/cache/fetchers/split_fetcher.rb @@ -26,7 +26,7 @@ def fetch_splits(fetch_options = { cache_control_headers: false, till: nil }) @semaphore.synchronize do data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options) SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config, @splits_api.clear_storage) - SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config, @splits_api.clear_storage) + SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config) @splits_repository.set_segment_names(data[:segment_names]) @rule_based_segments_repository.set_segment_names(data[:segment_names]) @config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index e98e0d84..bc763c36 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -127,6 +127,10 @@ def clear @tt_cache.clear @adapter.clear(namespace_key) + unless @config.mode.equal?(:consumer) + @adapter.set_string(namespace_key('.splits.till'), '-1') + @adapter.initialize_map(namespace_key('.segments.registered')) + end end def kill(change_number, split_name, default_treatment) diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index 45c1b30f..6146d387 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -22,7 +22,6 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till start = Time.now if check_last_proxy_check_timestamp - puts "switching to new spec" @spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION @config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.") since = -1 diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index be0e63c8..d66bac33 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -27,7 +27,7 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, feature_flag_repository.update(to_add, to_delete, change_number) end - def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config, clear_storage) + def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config) to_add = [] to_delete = [] rule_based_segments.each do |rule_based_segment| @@ -40,7 +40,6 @@ def self.update_rule_based_segment_repository(rule_based_segment_repository, rul config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled to_add.push(rule_based_segment) end - rule_based_segment_repository.clear if clear_storage rule_based_segment_repository.update(to_add, to_delete, change_number) end diff --git a/lib/splitclient-rb/sse/workers/splits_worker.rb b/lib/splitclient-rb/sse/workers/splits_worker.rb index 88b06660..a6ba1696 100644 --- a/lib/splitclient-rb/sse/workers/splits_worker.rb +++ b/lib/splitclient-rb/sse/workers/splits_worker.rb @@ -93,7 +93,7 @@ def update_rule_based_segment(notification) new_rb_segment = return_object_from_json(notification) SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository, [new_rb_segment], - notification.data['changeNumber'], @config, false) + notification.data['changeNumber'], @config) fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_rb_segment, "IN_SEGMENT"), @rule_based_segment_repository) # TODO: enable when telemetry spec is added diff --git a/spec/integrations/in_memory_client_spec.rb b/spec/integrations/in_memory_client_spec.rb index ab0538ae..2f2024b2 100644 --- a/spec/integrations/in_memory_client_spec.rb +++ b/spec/integrations/in_memory_client_spec.rb @@ -1381,6 +1381,44 @@ expect(client_rbs.get_treatment('mauro@split.io', 'rbs_feature_flag', {:email => 'mauro@split.io'})).to eq('off') end end + + context 'old spec tests' do + let(:old_spec_splits) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../test_data/rule_based_segments/split_old_spec.json'))) } + + it 'check new spec after last proxy timestamp expires' do + splits_rbs = File.read(File.join(SplitIoClient.root, 'spec/test_data/rule_based_segments/rule_base_segments.json')) + + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return({status: 400, body: ''}, {status: 200, body: splits_rbs}) + stub_request(:get, "https://sdk.split.io/api/splitChanges?rbSince=1506703262916&s=1.3&since=1506703262916") + .to_return(status: 200, body: '') + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1') + .to_return(status: 200, body: old_spec_splits) + stub_request(:get, "https://proxy-server/api/splitChanges?s=1.1&since=1457726098069") + .to_return(status: 200, body: '') + stub_request(:post, "https://telemetry.split.io/api/v1/metrics/config") + .to_return(status: 200, body: '') + + factory_old_spec = + SplitIoClient::SplitFactory.new('test_api_key', + {impressions_mode: :none, + features_refresh_rate: 2, + base_uri: "https://proxy-server/api", + streaming_enabled: false}) + + SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1 + client_old_spec = factory_old_spec.client + client_old_spec.block_until_ready + expect(client_old_spec.get_treatment('whitelisted_user', 'whitelist_feature')).to eq('on') + + sleep 1 + split_fetcher = factory_old_spec.instance_variable_get(:@split_fetcher) + split_fetcher.fetch_splits + sleep 1 + expect(client_old_spec.get_treatment('bilal@split.io', 'rbs_feature_flag', {:email => 'bilal@split.io'})).to eq('on') + expect(client_old_spec.get_treatment('whitelisted_user', 'whitelist_feature')).to eq('control') + end + end end private diff --git a/spec/integrations/old_spec_client_spec.rb b/spec/integrations/old_spec_client_spec.rb deleted file mode 100644 index 7ee2526c..00000000 --- a/spec/integrations/old_spec_client_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe SplitIoClient do - context 'old spec tests' do - let(:old_spec_splits) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../test_data/rule_based_segments/split_old_spec.json'))) } - - it 'check new spec after last proxy timestamp expires' do - splits_rbs = File.read(File.join(SplitIoClient.root, 'spec/test_data/rule_based_segments/rule_base_segments.json')) - - stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1') - .to_return({status: 400, body: ''}, {status: 200, body: splits_rbs}) - stub_request(:get, "https://sdk.split.io/api/splitChanges?rbSince=1506703262916&s=1.3&since=1506703262916") - .to_return(status: 200, body: '') - stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1') - .to_return(status: 200, body: old_spec_splits) - stub_request(:get, "https://proxy-server/api/splitChanges?s=1.1&since=1457726098069") - .to_return(status: 200, body: '') - stub_request(:post, "https://telemetry.split.io/api/v1/metrics/config") - .to_return(status: 200, body: '') - - factory_old_spec = - SplitIoClient::SplitFactory.new('test_api_key', - {impressions_mode: :none, - features_refresh_rate: 2, - base_uri: "https://proxy-server/api", - streaming_enabled: false}) - - SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1 - client_old_spec = factory_old_spec.client - client_old_spec.block_until_ready - expect(client_old_spec.get_treatment('whitelisted_user', 'whitelist_feature')).to eq('on') - - sleep 1 - split_fetcher = factory_old_spec.instance_variable_get(:@split_fetcher) - split_fetcher.fetch_splits - sleep 1 - expect(client_old_spec.get_treatment('bilal@split.io', 'rbs_feature_flag', {:email => 'bilal@split.io'})).to eq('on') - end - end -end From b7013d49ec2e8fc36f1adc6d8b48a421eca47751 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 15 May 2025 11:49:35 -0700 Subject: [PATCH 21/44] polish --- .../cache/repositories/splits_repository.rb | 17 +++---- lib/splitclient-rb/engine/api/splits.rb | 10 ++++ .../matchers/rule_based_segment_matcher.rb | 46 ++++++++++-------- .../helpers/evaluator_helper.rb | 4 +- .../helpers/repository_helper.rb | 17 ++++--- lib/splitclient-rb/helpers/util.rb | 3 +- .../sse/workers/splits_worker.rb | 48 ++++++++++--------- 7 files changed, 88 insertions(+), 57 deletions(-) diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index bc763c36..2fa7c396 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -43,10 +43,7 @@ def initialize(config, flag_sets_repository, flag_set_filter) end @flag_sets = flag_sets_repository @flag_set_filter = flag_set_filter - unless @config.mode.equal?(:consumer) - @adapter.set_string(namespace_key('.splits.till'), '-1') - @adapter.initialize_map(namespace_key('.segments.registered')) - end + initialize_keys end def update(to_add, to_delete, new_change_number) @@ -127,10 +124,7 @@ def clear @tt_cache.clear @adapter.clear(namespace_key) - unless @config.mode.equal?(:consumer) - @adapter.set_string(namespace_key('.splits.till'), '-1') - @adapter.initialize_map(namespace_key('.segments.registered')) - end + initialize_keys end def kill(change_number, split_name, default_treatment) @@ -171,6 +165,13 @@ def flag_set_filter private + def initialize_keys + unless @config.mode.equal?(:consumer) + @adapter.set_string(namespace_key('.splits.till'), '-1') + @adapter.initialize_map(namespace_key('.segments.registered')) + end + end + def add_feature_flag(split) return unless split[:name] existing_split = get_split(split[:name]) diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index 6146d387..b8e08974 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -59,6 +59,7 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till result = convert_to_newSPEC(result) end + result[:rbs][:d] = check_rbs_data(result[:rbs][:d]) result = objects_with_segment_names(result) if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION @@ -97,6 +98,15 @@ def clear_storage private + def check_rbs_data(rbs_data) + rbs_data.each do |rb_segment| + rb_segment[:excluded] = {:keys => [], :segments => []} if rb_segment[:excluded].nil? + rb_segment[:excluded][:keys] = [] if rb_segment[:excluded][:keys].nil? + rb_segment[:excluded][:segments] = [] if rb_segment[:excluded][:segments].nil? + end + rbs_data + end + def objects_with_segment_names(parsed_objects) parsed_objects[:segment_names] = Set.new parsed_objects[:segment_names] = diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index 69c72272..081c657c 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -25,20 +25,10 @@ def match?(args) rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name) return false if rule_based_segment.nil? - if args[:value].nil? or args[:value].empty? - key = args[:matching_key] - else - key = args[:value] - end + key = update_key(args) return false if rule_based_segment[:excluded][:keys].include?(key) - rule_based_segment[:excluded][:segments].each do |segment| - return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], key) - - if segment[:type] == 'rule-based' - return false if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args) - end - end + return false unless check_excluded_segments(rule_based_segment) matches = false rule_based_segment[:conditions].each do |c| @@ -54,13 +44,31 @@ def match?(args) private - def match_rbs(rule_based_segment, args) - rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, rule_based_segment[:name], @config) - return rbs_matcher.match?( - matching_key: args[:matching_key], - bucketing_key: args[:value], - attributes: args[:attributes] - ) + def check_excluded_segments(rule_based_segment) + rule_based_segment[:excluded][:segments].each do |segment| + return false if segment[:type] == 'standard' && @segments_repository.in_segment?(segment[:name], key) + + return false if segment[:type] == 'rule-based' && match_rbs( + @rule_based_segments_repository.get_rule_based_segment(segment[:name]), args + ) + end + True + end + + def update_key(args) + if args[:value].nil? || args[:value].empty? + args[:matching_key] + else + args[:value] + end + end + + def match_rbs(rule_based_segment, args) + rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository, + rule_based_segment[:name], @config) + rbs_matcher.match?(matching_key: args[:matching_key], + bucketing_key: args[:value], + attributes: args[:attributes]) end end end diff --git a/lib/splitclient-rb/helpers/evaluator_helper.rb b/lib/splitclient-rb/helpers/evaluator_helper.rb index 331c7987..2b16e094 100644 --- a/lib/splitclient-rb/helpers/evaluator_helper.rb +++ b/lib/splitclient-rb/helpers/evaluator_helper.rb @@ -8,7 +8,9 @@ def self.matcher_type(condition, segments_repository, rb_segment_repository) segments_repository.adapter.pipelined do condition.matchers.each do |matcher| matchers << if matcher[:negate] - condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository)) + condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, + matcher, segments_repository, + rb_segment_repository)) else matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository) end diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index d66bac33..eb790447 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -13,12 +13,7 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, next end - unless feature_flag.key?(:impressionsDisabled) - feature_flag[:impressionsDisabled] = false - if config.debug_enabled - config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false") - end - end + feature_flag = self.check_impressions_disabled(feature_flag, config) config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled to_add.push(feature_flag) @@ -27,6 +22,16 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, feature_flag_repository.update(to_add, to_delete, change_number) end + def self.check_impressions_disabled(feature_flag, config) + unless feature_flag.key?(:impressionsDisabled) + feature_flag[:impressionsDisabled] = false + if config.debug_enabled + config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false") + end + end + feature_flag + end + def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config) to_add = [] to_delete = [] diff --git a/lib/splitclient-rb/helpers/util.rb b/lib/splitclient-rb/helpers/util.rb index c9b356c4..0f61d0ca 100644 --- a/lib/splitclient-rb/helpers/util.rb +++ b/lib/splitclient-rb/helpers/util.rb @@ -6,7 +6,8 @@ class Util def self.segment_names_by_object(object, matcher_type) object[:conditions].each_with_object(Set.new) do |condition, names| condition[:matcherGroup][:matchers].each do |matcher| - next if matcher[:userDefinedSegmentMatcherData].nil? or matcher[:matcherType] != matcher_type + next if matcher[:userDefinedSegmentMatcherData].nil? || matcher[:matcherType] != matcher_type + names << matcher[:userDefinedSegmentMatcherData][:segmentName] end end diff --git a/lib/splitclient-rb/sse/workers/splits_worker.rb b/lib/splitclient-rb/sse/workers/splits_worker.rb index a6ba1696..27ea916e 100644 --- a/lib/splitclient-rb/sse/workers/splits_worker.rb +++ b/lib/splitclient-rb/sse/workers/splits_worker.rb @@ -4,7 +4,8 @@ module SplitIoClient module SSE module Workers class SplitsWorker - def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segment_repository) + def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime_producer, + segment_fetcher, rule_based_segment_repository) @synchronizer = synchronizer @config = config @feature_flags_repository = feature_flags_repository @@ -68,12 +69,11 @@ def perform def update_feature_flag(notification) return true if @feature_flags_repository.get_change_number.to_i >= notification.data['changeNumber'] return false unless !notification.data['d'].nil? && @feature_flags_repository.get_change_number == notification.data['pcn'] - new_split = return_object_from_json(notification) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository, - [new_split], - notification.data['changeNumber'], @config, false) - fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_SEGMENT"), @feature_flags_repository) - if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_RULE_BASED_SEGMENT"), notification.data['changeNumber']) + + update_feature_flag_repository(notification) + fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_SEGMENT'), @feature_flags_repository) + if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_RULE_BASED_SEGMENT'), + notification.data['changeNumber']) return true end @@ -86,18 +86,26 @@ def update_feature_flag(notification) false end + def update_feature_flag_repository(notification) + new_split = return_object_from_json(notification) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository, [new_split], + notification.data['changeNumber'], @config, false) + end + def update_rule_based_segment(notification) return true if @rule_based_segment_repository.get_change_number.to_i >= notification.data['changeNumber'] - return false unless !notification.data['d'].nil? && @rule_based_segment_repository.get_change_number == notification.data['pcn'] + return false unless !notification.data['d'].nil? && + @rule_based_segment_repository.get_change_number == notification.data['pcn'] new_rb_segment = return_object_from_json(notification) SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository, - [new_rb_segment], - notification.data['changeNumber'], @config) - fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_rb_segment, "IN_SEGMENT"), @rule_based_segment_repository) + [new_rb_segment], + notification.data['changeNumber'], @config) + fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_rb_segment, 'IN_SEGMENT'), + @rule_based_segment_repository) -# TODO: enable when telemetry spec is added -# @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS) + # TODO: enable when telemetry spec is added + # @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS) true rescue StandardError => e @@ -110,11 +118,9 @@ def kill_feature_flag(notification) return if @feature_flags_repository.get_change_number.to_i > notification.data['changeNumber'] @config.logger.debug("feature_flags_worker kill #{notification.data['splitName']}, #{notification.data['changeNumber']}") - @feature_flags_repository.kill( - notification.data['changeNumber'], - notification.data['splitName'], - notification.data['defaultTreatment'] - ) + @feature_flags_repository.kill(notification.data['changeNumber'], + notification.data['splitName'], + notification.data['defaultTreatment']) @synchronizer.fetch_splits(notification.data['changeNumber'], 0) end @@ -124,7 +130,6 @@ def return_object_from_json(notification) end def fetch_segments_if_not_exists(segment_names, object_repository) - return if segment_names.nil? object_repository.set_segment_names(segment_names) @@ -132,9 +137,8 @@ def fetch_segments_if_not_exists(segment_names, object_repository) end def fetch_rule_based_segments_if_not_exists(segment_names, change_number) - if segment_names.nil? or segment_names.empty? or @rule_based_segment_repository.contains?(segment_names.to_a) - return false - end + return false if segment_names.nil? || segment_names.empty? || @rule_based_segment_repository.contains?(segment_names.to_a) + @synchronizer.fetch_splits(0, change_number) true From 1952d9d0da9e59e0f0bb9ddb13edb9e199709e26 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 16 May 2025 10:04:14 -0700 Subject: [PATCH 22/44] polish --- lib/splitclient-rb/helpers/repository_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index eb790447..da72ad22 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -13,7 +13,7 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, next end - feature_flag = self.check_impressions_disabled(feature_flag, config) + feature_flag = check_impressions_disabled(feature_flag, config) config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled to_add.push(feature_flag) From a2ce544eaa88905290d622953ae5fba2285690b6 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 16 May 2025 10:05:02 -0700 Subject: [PATCH 23/44] fix sse issue --- .../engine/matchers/rule_based_segment_matcher.rb | 6 +++--- lib/splitclient-rb/helpers/repository_helper.rb | 2 +- lib/splitclient-rb/sse/notification_processor.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index 081c657c..74c5e2f4 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -28,7 +28,7 @@ def match?(args) key = update_key(args) return false if rule_based_segment[:excluded][:keys].include?(key) - return false unless check_excluded_segments(rule_based_segment) + return false unless check_excluded_segments(rule_based_segment, key, args) matches = false rule_based_segment[:conditions].each do |c| @@ -44,7 +44,7 @@ def match?(args) private - def check_excluded_segments(rule_based_segment) + def check_excluded_segments(rule_based_segment, key, args) rule_based_segment[:excluded][:segments].each do |segment| return false if segment[:type] == 'standard' && @segments_repository.in_segment?(segment[:name], key) @@ -52,7 +52,7 @@ def check_excluded_segments(rule_based_segment) @rule_based_segments_repository.get_rule_based_segment(segment[:name]), args ) end - True + true end def update_key(args) diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index eb790447..da72ad22 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -13,7 +13,7 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, next end - feature_flag = self.check_impressions_disabled(feature_flag, config) + feature_flag = check_impressions_disabled(feature_flag, config) config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled to_add.push(feature_flag) diff --git a/lib/splitclient-rb/sse/notification_processor.rb b/lib/splitclient-rb/sse/notification_processor.rb index d723cbdc..af5563c3 100644 --- a/lib/splitclient-rb/sse/notification_processor.rb +++ b/lib/splitclient-rb/sse/notification_processor.rb @@ -27,7 +27,7 @@ def process(incoming_notification) private def process_split_update(notification) - @config.logger.debug("#{notification.type} notification received: #{notification}") if @config.debug_enabled + @config.logger.debug("#{notification.event_type} notification received: #{notification}") if @config.debug_enabled @splits_worker.add_to_queue(notification) end From 1566bfbad6af04f38cc3173dcee81537d9d6e75e Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 16 May 2025 10:32:53 -0700 Subject: [PATCH 24/44] polish --- .../sse/workers/splits_worker.rb | 3 +- spec/engine/api/splits_spec.rb | 4 +- .../rule_based_segment_matcher_spec.rb | 4 +- spec/repository_helper.rb | 37 +++++++++++++------ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/lib/splitclient-rb/sse/workers/splits_worker.rb b/lib/splitclient-rb/sse/workers/splits_worker.rb index 27ea916e..f1e83775 100644 --- a/lib/splitclient-rb/sse/workers/splits_worker.rb +++ b/lib/splitclient-rb/sse/workers/splits_worker.rb @@ -70,7 +70,7 @@ def update_feature_flag(notification) return true if @feature_flags_repository.get_change_number.to_i >= notification.data['changeNumber'] return false unless !notification.data['d'].nil? && @feature_flags_repository.get_change_number == notification.data['pcn'] - update_feature_flag_repository(notification) + new_split = update_feature_flag_repository(notification) fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_SEGMENT'), @feature_flags_repository) if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_RULE_BASED_SEGMENT'), notification.data['changeNumber']) @@ -90,6 +90,7 @@ def update_feature_flag_repository(notification) new_split = return_object_from_json(notification) SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository, [new_split], notification.data['changeNumber'], @config, false) + new_split end def update_rule_based_segment(notification) diff --git a/spec/engine/api/splits_spec.rb b/spec/engine/api/splits_spec.rb index 2d293498..9c384a32 100644 --- a/spec/engine/api/splits_spec.rb +++ b/spec/engine/api/splits_spec.rb @@ -206,7 +206,7 @@ parsed_splits = splits_api.since(-1, -1) - expect(parsed_splits[:ff][:d].length()).to eq(7) + expect(parsed_splits[:ff][:d].length()).to eq(6) expect(parsed_splits[:ff][:t]).to eq(1457726098069) expect(parsed_splits[:ff][:s]).to eq(-1) expect(parsed_splits[:rbs]).to eq({:d => [], :s => -1, :t => -1}) @@ -220,7 +220,7 @@ .to_return(status: 200, body: old_spec_splits) parsed_splits = splits_api.since(-1, -1) - expect(parsed_splits[:ff][:d].length()).to eq(7) + expect(parsed_splits[:ff][:d].length()).to eq(6) expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Api::Splits::SPEC_1_1) SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1 diff --git a/spec/engine/matchers/rule_based_segment_matcher_spec.rb b/spec/engine/matchers/rule_based_segment_matcher_spec.rb index f09b5422..ce2f9aba 100644 --- a/spec/engine/matchers/rule_based_segment_matcher_spec.rb +++ b/spec/engine/matchers/rule_based_segment_matcher_spec.rb @@ -32,7 +32,7 @@ expect(matcher.match?(value: 'key2')).to be false end - it 'return true if excluded rb segment is matched' do + it 'return false if excluded rb segment is matched' do rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) rbs = {:name => 'sample_rule_based_segment', :trafficTypeName => 'tt_name_1', :conditions => [], :excluded => {:keys => [], :segments => [{:name => 'no_excludes', :type => 'rule-based'}]}} rbs2 = {:name => 'no_excludes', :trafficTypeName => 'tt_name_1', @@ -60,7 +60,7 @@ rbs_repositoy.update([rbs, rbs2], [], -1) matcher = described_class.new(segments_repository, rbs_repositoy, 'sample_rule_based_segment', config) - expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be true + expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be false expect(matcher.match?(value: 'bilal', attributes: {'email': 'bilal'})).to be false end diff --git a/spec/repository_helper.rb b/spec/repository_helper.rb index 0f091f78..d38dff96 100644 --- a/spec/repository_helper.rb +++ b/spec/repository_helper.rb @@ -13,16 +13,16 @@ flag_sets_repository, flag_set_filter) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config, false) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => ['set_3']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => ['set_3']}], -1, config, false) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => ['set_1']}], -1, config, false) expect(feature_flag_repository.get_split('split1').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', conditions: [], :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', conditions: [], :sets => ['set_1']}], -1, config, false) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) end @@ -35,16 +35,16 @@ flag_sets_repository, flag_set_filter) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config, false) expect(feature_flag_repository.get_split('split1').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :sets => ['set_3']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :sets => ['set_3']}], -1, config, false) expect(feature_flag_repository.get_split('split2').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split3', :status => 'ACTIVE', conditions: [], :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split3', :status => 'ACTIVE', conditions: [], :sets => ['set_1']}], -1, config, false) expect(feature_flag_repository.get_split('split1').nil?).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', conditions: [], :sets => ['set_1']}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ARCHIVED', conditions: [], :sets => ['set_1']}], -1, config, false) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) end @@ -57,16 +57,31 @@ flag_sets_repository, flag_set_filter) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config, false) expect(feature_flag_repository.get_split('split1')[:impressionsDisabled]).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :impressionsDisabled => false, :sets => []}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :impressionsDisabled => false, :sets => []}], -1, config, false) expect(feature_flag_repository.get_split('split2')[:impressionsDisabled]).to eq(false) - SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :impressionsDisabled => true, :sets => []}], -1, config) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :impressionsDisabled => true, :sets => []}], -1, config, false) expect(feature_flag_repository.get_split('split2')[:impressionsDisabled]).to eq(true) + end + + it 'test clear cache flag' do + config = SplitIoClient::SplitConfig.new(cache_adapter: :memory) + flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) + flag_sets_repository = SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([]) + feature_flag_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new( + config, + flag_sets_repository, + flag_set_filter) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config, false) + expect(feature_flag_repository.get_split('split1').nil?).to eq(false) + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :sets => ['set_3']}], -1, config, true) + expect(feature_flag_repository.get_split('split2').nil?).to eq(false) + expect(feature_flag_repository.get_split('split1').nil?).to eq(true) end end end From ecd007b8f34868b5ddf975be9d1dd550fbb8617f Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 16 May 2025 12:15:51 -0700 Subject: [PATCH 25/44] updated split api --- lib/splitclient-rb/engine/api/splits.rb | 4 ++ spec/engine/api/splits_spec.rb | 29 +++++++- .../rule_based_segments/split_old_spec2.json | 66 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 spec/test_data/rule_based_segments/split_old_spec2.json diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index b8e08974..1b690e3b 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -16,6 +16,7 @@ def initialize(api_key, config, telemetry_runtime_producer) @spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION @last_proxy_check_timestamp = 0 @clear_storage = false + @old_spec_since = nil end def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil}) @@ -24,13 +25,16 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till if check_last_proxy_check_timestamp @spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION @config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.") + @old_spec_since = since since = -1 since_rbs = -1 fetch_options = { cache_control_headers: false, till: nil, sets: nil} end if @spec_version == Splits::SPEC_1_1 + since = @old_spec_since unless @old_spec_since.nil? params = { s: @spec_version, since: since } + @old_spec_since = nil else params = { s: @spec_version, since: since, rbSince: since_rbs } end diff --git a/spec/engine/api/splits_spec.rb b/spec/engine/api/splits_spec.rb index 9c384a32..e5c4a47f 100644 --- a/spec/engine/api/splits_spec.rb +++ b/spec/engine/api/splits_spec.rb @@ -225,11 +225,38 @@ SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1 sleep 1 - parsed_splits = splits_api.since(-1, -1) + parsed_splits = splits_api.since(1457726098069, -1) expect(splits_api.clear_storage).to eq(true) expect(parsed_splits[:ff][:d].length()).to eq(2) expect(parsed_splits[:rbs][:d].length()).to eq(1) expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Spec::FeatureFlags::SPEC_VERSION) + expect(splits_api.instance_variable_get(:@old_spec_since)).to eq(1457726098069) + end + + it 'check using old_spec_since variable' do + old_spec_splits2 = File.read(File.expand_path(File.join(File.dirname(__FILE__), '../../test_data/rule_based_segments/split_old_spec2.json'))) + + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1') + .to_return({status: 400, body: ''}, {status: 400, body: ''}) + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1') + .to_return(status: 200, body: old_spec_splits) + stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=1457726098069') + .to_return(status: 200, body: old_spec_splits2) + + parsed_splits = splits_api.since(-1, -1) + expect(parsed_splits[:ff][:d].length()).to eq(6) + expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Api::Splits::SPEC_1_1) + + SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1 + sleep 1 + parsed_splits = splits_api.since(1457726098069, -1) + SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 100000 + + sleep 1 + expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Api::Splits::SPEC_1_1) + expect(splits_api.instance_variable_get(:@old_spec_since)).to eq(nil) + expect(parsed_splits[:ff][:d].length()).to eq(1) + expect(log.string).to include 'Switching to new Feature flag spec 1.3 and fetching.' end end end diff --git a/spec/test_data/rule_based_segments/split_old_spec2.json b/spec/test_data/rule_based_segments/split_old_spec2.json new file mode 100644 index 00000000..74aff1ed --- /dev/null +++ b/spec/test_data/rule_based_segments/split_old_spec2.json @@ -0,0 +1,66 @@ +{ + "splits": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "new_feature", + "seed": -1222652054, + "status": "ACTIVE", + "killed": false, + "changeNumber": 123, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "whitelisted_user" + ] + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ] + }, + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ] + } + ], + "sets": ["set1", "set2"] + } ], + "since": -1, + "till": 1457726098069 +} \ No newline at end of file From 3a33ffcfabcf9f2d92b579e3f8cd5115a3a03e35 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Mon, 19 May 2025 15:45:46 -0700 Subject: [PATCH 26/44] updated util and test --- lib/splitclient-rb/engine/api/splits.rb | 10 +-------- lib/splitclient-rb/helpers/util.rb | 11 ++++++++++ .../sse/workers/splits_worker.rb | 2 +- spec/sse/workers/splits_worker_spec.rb | 21 ++++++++++++------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index b8e08974..a86397e1 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -115,15 +115,7 @@ def objects_with_segment_names(parsed_objects) end.flatten parsed_objects[:rbs][:d].each do |rule_based_segment| - parsed_objects[:segment_names].merge Helpers::Util.segment_names_by_object(rule_based_segment, "IN_SEGMENT") - end - - parsed_objects[:rbs][:d].each do |rule_based_segment| - rule_based_segment[:excluded][:segments].each do |segment| - if segment[:type] == "standard" - parsed_objects[:segment_names].add(segment[:name]) - end - end + parsed_objects[:segment_names].merge Helpers::Util.segment_names_in_rb_segment(rule_based_segment, "IN_SEGMENT") end parsed_objects diff --git a/lib/splitclient-rb/helpers/util.rb b/lib/splitclient-rb/helpers/util.rb index 0f61d0ca..3c6eff85 100644 --- a/lib/splitclient-rb/helpers/util.rb +++ b/lib/splitclient-rb/helpers/util.rb @@ -12,6 +12,17 @@ def self.segment_names_by_object(object, matcher_type) end end end + + def self.segment_names_in_rb_segment(object, matcher_type) + names = Set.new + names.merge segment_names_by_object(object, matcher_type) + object[:excluded][:segments].each do |segment| + if segment[:type] == 'standard' + names.add(segment[:name]) + end + end + names + end end end end diff --git a/lib/splitclient-rb/sse/workers/splits_worker.rb b/lib/splitclient-rb/sse/workers/splits_worker.rb index f1e83775..4fe7563e 100644 --- a/lib/splitclient-rb/sse/workers/splits_worker.rb +++ b/lib/splitclient-rb/sse/workers/splits_worker.rb @@ -102,7 +102,7 @@ def update_rule_based_segment(notification) SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository, [new_rb_segment], notification.data['changeNumber'], @config) - fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_rb_segment, 'IN_SEGMENT'), + fetch_segments_if_not_exists(Helpers::Util.segment_names_in_rb_segment(new_rb_segment, 'IN_SEGMENT'), @rule_based_segment_repository) # TODO: enable when telemetry spec is added diff --git a/spec/sse/workers/splits_worker_spec.rb b/spec/sse/workers/splits_worker_spec.rb index 0ef724f9..1bc41bf8 100644 --- a/spec/sse/workers/splits_worker_spec.rb +++ b/spec/sse/workers/splits_worker_spec.rb @@ -22,7 +22,7 @@ let(:event_split_update_no_definition) { SplitIoClient::SSE::EventSource::StreamData.new("data", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":5564531221,"pcn":1234,"c": 0, "d":null}'), 'test') } let(:event_split_update_segments) { SplitIoClient::SSE::EventSource::StreamData.new("data", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":5564531221,"pcn":1234,"c":2,"d":"eJzcVFtr20wQ/SvhPK9AsnzTvpnPJp+po0DlppRgzFga2dusJLNapaRG/73Id7sOoU+FvmluZ3TOGXYDayhNVTx9W3NIGUOiKtlAQCWQSNq+FyeJ6yzcBTuex+T0qe86XrfrUkJBzH4AgXw3mVFlivl3eiWIA/BA6yImq4oc0nPdG/mIOYF0gpYfeO3AEyh3Ca/XDfxer+u2BUpLtiohMfhvOn4aQeBFad20paRLFkg4pUrbqWGyGecWEvbwPQ9cCMQrypccVtmCDaTX7feCnu+7nY7nCZBeFpAtgbjIU7WszPbPSshNvc0lah8/b05hoxkkvv4/no4m42gKgYxsvGJzb4pqDdn0ZguVNwsxCIenhh3SPriBk/OSLB/Z/Vgpy1qV9mE3MSRLDfwxD/kMSjKVb1dUpmgwVFxgVtezWmBNxp5RsDdlavkdCJTqJ2+tqmcCmhasIU+LOEEtftfg8+Nk8vjlzxV44beINce2ME3z2TEeDrEWVzKNw3k0un8YhTd0aiaGnKqck4iXDakrwcpdNjzdq9PChxIV+VEXt2F/UUvTC9Guyk/t90dfO+/Xro73w65z7y6cU/ndnvTdge7f9W8wmcw/jb5F1+79yybsX6c7U2lGPat/BQAA//9ygdKB"}'), 'test') } let(:event_split_update_rb_segments) { SplitIoClient::SSE::EventSource::StreamData.new("data", 123, JSON.parse('{"type":"SPLIT_UPDATE","changeNumber":5564531221,"pcn":1234,"c":0,"d":"eyJjaGFuZ2VOdW1iZXIiOiAxMCwgInRyYWZmaWNUeXBlTmFtZSI6ICJ1c2VyIiwgIm5hbWUiOiAicmJzX2ZsYWciLCAidHJhZmZpY0FsbG9jYXRpb24iOiAxMDAsICJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOiAxODI4Mzc3MzgwLCAic2VlZCI6IC0yODY2MTc5MjEsICJzdGF0dXMiOiAiQUNUSVZFIiwgImtpbGxlZCI6IGZhbHNlLCAiZGVmYXVsdFRyZWF0bWVudCI6ICJvZmYiLCAiYWxnbyI6IDIsICJjb25kaXRpb25zIjogW3siY29uZGl0aW9uVHlwZSI6ICJST0xMT1VUIiwgIm1hdGNoZXJHcm91cCI6IHsiY29tYmluZXIiOiAiQU5EIiwgIm1hdGNoZXJzIjogW3sia2V5U2VsZWN0b3IiOiB7InRyYWZmaWNUeXBlIjogInVzZXIifSwgIm1hdGNoZXJUeXBlIjogIklOX1JVTEVfQkFTRURfU0VHTUVOVCIsICJuZWdhdGUiOiBmYWxzZSwgInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjogeyJzZWdtZW50TmFtZSI6ICJzYW1wbGVfcnVsZV9iYXNlZF9zZWdtZW50In19XX0sICJwYXJ0aXRpb25zIjogW3sidHJlYXRtZW50IjogIm9uIiwgInNpemUiOiAxMDB9LCB7InRyZWF0bWVudCI6ICJvZmYiLCAic2l6ZSI6IDB9XSwgImxhYmVsIjogImluIHJ1bGUgYmFzZWQgc2VnbWVudCBzYW1wbGVfcnVsZV9iYXNlZF9zZWdtZW50In0sIHsiY29uZGl0aW9uVHlwZSI6ICJST0xMT1VUIiwgIm1hdGNoZXJHcm91cCI6IHsiY29tYmluZXIiOiAiQU5EIiwgIm1hdGNoZXJzIjogW3sia2V5U2VsZWN0b3IiOiB7InRyYWZmaWNUeXBlIjogInVzZXIifSwgIm1hdGNoZXJUeXBlIjogIkFMTF9LRVlTIiwgIm5lZ2F0ZSI6IGZhbHNlfV19LCAicGFydGl0aW9ucyI6IFt7InRyZWF0bWVudCI6ICJvbiIsICJzaXplIjogMH0sIHsidHJlYXRtZW50IjogIm9mZiIsICJzaXplIjogMTAwfV0sICJsYWJlbCI6ICJkZWZhdWx0IHJ1bGUifV0sICJjb25maWd1cmF0aW9ucyI6IHt9LCAic2V0cyI6IFtdLCAiaW1wcmVzc2lvbnNEaXNhYmxlZCI6IGZhbHNlfQ=="}'), 'test') } - let(:event_rb_segment_update) { SplitIoClient::SSE::EventSource::StreamData.new("data", 12345, JSON.parse('{"type":"RB_SEGMENT_UPDATE","changeNumber":5564531221,"pcn":1234,"c":0,"d":"eyJjaGFuZ2VOdW1iZXIiOiA1LCAibmFtZSI6ICJzYW1wbGVfcnVsZV9iYXNlZF9zZWdtZW50IiwgInN0YXR1cyI6ICJBQ1RJVkUiLCAidHJhZmZpY1R5cGVOYW1lIjogInVzZXIiLCAiZXhjbHVkZWQiOiB7ImtleXMiOiBbIm1hdXJvQHNwbGl0LmlvIl0sICJzZWdtZW50cyI6IFtdfSwgImNvbmRpdGlvbnMiOiBbeyJtYXRjaGVyR3JvdXAiOiB7ImNvbWJpbmVyIjogIkFORCIsICJtYXRjaGVycyI6IFt7ImtleVNlbGVjdG9yIjogeyJ0cmFmZmljVHlwZSI6ICJ1c2VyIiwgImF0dHJpYnV0ZSI6ICJlbWFpbCJ9LCAibWF0Y2hlclR5cGUiOiAiRU5EU19XSVRIIiwgIm5lZ2F0ZSI6IGZhbHNlLCAid2hpdGVsaXN0TWF0Y2hlckRhdGEiOiB7IndoaXRlbGlzdCI6IFsiQHNwbGl0LmlvIl19fV19fV19"}'), 'test') } + let(:event_rb_segment_update) { SplitIoClient::SSE::EventSource::StreamData.new("data", 12345, JSON.parse('{"type":"RB_SEGMENT_UPDATE","changeNumber":5564531221,"pcn":1234,"c":0,"d":"eyJjaGFuZ2VOdW1iZXIiOiA1LCAibmFtZSI6ICJzYW1wbGVfcnVsZV9iYXNlZF9zZWdtZW50IiwgInN0YXR1cyI6ICJBQ1RJVkUiLCAidHJhZmZpY1R5cGVOYW1lIjogInVzZXIiLCAiZXhjbHVkZWQiOiB7ImtleXMiOiBbIm1hdXJvQHNwbGl0LmlvIl0sICJzZWdtZW50cyI6IFt7InR5cGUiOiAic3RhbmRhcmQiLCAibmFtZSI6ICJzZWdtZW50MSJ9XX0sICJjb25kaXRpb25zIjogW3sibWF0Y2hlckdyb3VwIjogeyJjb21iaW5lciI6ICJBTkQiLCAibWF0Y2hlcnMiOiBbeyJrZXlTZWxlY3RvciI6IHsidHJhZmZpY1R5cGUiOiAidXNlciIsICJhdHRyaWJ1dGUiOiAiZW1haWwifSwgIm1hdGNoZXJUeXBlIjogIklOX1NFR01FTlQiLCAibmVnYXRlIjogZmFsc2UsICJ1c2VyRGVmaW5lZFNlZ21lbnRNYXRjaGVyRGF0YSI6IHsic2VnbWVudE5hbWUiOiAiZGVtbyJ9fV19fV19"}'), 'test') } context 'add change number to queue' do let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])} @@ -309,7 +309,7 @@ "trafficTypeName": "user", "excluded":{ "keys":["mauro@split.io","gaston@split.io"], - "segments":[] + "segments":["segment1"] }, "conditions": [ { @@ -321,13 +321,11 @@ "trafficType": "user", "attribute": "email" }, - "matcherType": "ENDS_WITH", + "matcherType": "IN_SEGMENT", "negate": false, - "whitelistMatcherData": { - "whitelist": [ - "@split.io" - ] - } + "userDefinedSegmentMatcherData":{ + "segmentName":"demo" + }, } ] } @@ -358,6 +356,8 @@ end it 'process rb segment update' do + stub_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=-1').to_return(status: 200, body: '{"name":"maur-2","added":["admin"],"removed":[],"since":-1,"till":-1}}') + stub_request(:get, 'https://sdk.split.io/api/segmentChanges/demo?since=-1').to_return(status: 200, body: '{"name":"maur-2","added":["admin"],"removed":[],"since":-1,"till":-1}}') worker = subject.new(synchronizer, config, splits_repository, telemetry_runtime_producer, segment_fetcher, rule_based_segments_repository) worker.start @@ -366,6 +366,11 @@ sleep 2 rb_segment = rule_based_segments_repository.get_rule_based_segment("sample_rule_based_segment") expect(rb_segment[:name] == 'sample_rule_based_segment') + + expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/demo?since=-1')).to have_been_made.once + expect(segments_repository.used_segment_names[0]).to eq('demo') + expect(a_request(:get, 'https://sdk.split.io/api/segmentChanges/segment1?since=-1')).to have_been_made.once + expect(segments_repository.used_segment_names[1]).to eq('segment1') end end From 3fe83c5f4be2ab98dc53154c4c2cad937311cf02 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 20 May 2025 10:34:24 -0700 Subject: [PATCH 27/44] fix test --- spec/sse/workers/splits_worker_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/sse/workers/splits_worker_spec.rb b/spec/sse/workers/splits_worker_spec.rb index 1bc41bf8..74e00c51 100644 --- a/spec/sse/workers/splits_worker_spec.rb +++ b/spec/sse/workers/splits_worker_spec.rb @@ -309,7 +309,7 @@ "trafficTypeName": "user", "excluded":{ "keys":["mauro@split.io","gaston@split.io"], - "segments":["segment1"] + "segments":[{ "type": "standard", "name": "segment1"}] }, "conditions": [ { From d266ac9cfb7700820617dc4299c3cd976d3759d2 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 20 May 2025 10:55:30 -0700 Subject: [PATCH 28/44] polish --- lib/splitclient-rb.rb | 1 + .../engine/matchers/rule_based_segment_matcher.rb | 4 ++-- lib/splitclient-rb/engine/models/segment_type.rb | 4 ++++ lib/splitclient-rb/helpers/util.rb | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 lib/splitclient-rb/engine/models/segment_type.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index b6d04f55..9de3bc5e 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -105,6 +105,7 @@ require 'splitclient-rb/engine/metrics/binary_search_latency_tracker' require 'splitclient-rb/engine/models/split' require 'splitclient-rb/engine/models/label' +require 'splitclient-rb/engine/models/segment_type' require 'splitclient-rb/engine/models/treatment' require 'splitclient-rb/engine/auth_api_client' require 'splitclient-rb/engine/back_off' diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index 5143f606..a1cd9d61 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -44,9 +44,9 @@ def match?(args) def check_excluded_segments(rule_based_segment, key, args) rule_based_segment[:excluded][:segments].each do |segment| - return false if segment[:type] == 'standard' && @segments_repository.in_segment?(segment[:name], key) + return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD && @segments_repository.in_segment?(segment[:name], key) - return false if segment[:type] == 'rule-based' && match_rbs( + return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::RULE_BASED_SEGMENT && match_rbs( @rule_based_segments_repository.get_rule_based_segment(segment[:name]), args ) end diff --git a/lib/splitclient-rb/engine/models/segment_type.rb b/lib/splitclient-rb/engine/models/segment_type.rb new file mode 100644 index 00000000..48cfae9e --- /dev/null +++ b/lib/splitclient-rb/engine/models/segment_type.rb @@ -0,0 +1,4 @@ +class SplitIoClient::Engine::Models::SegmentType + STANDARD = 'standard' + RULE_BASED_SEGMENT = 'rule-based' +end diff --git a/lib/splitclient-rb/helpers/util.rb b/lib/splitclient-rb/helpers/util.rb index 3c6eff85..9a7de81c 100644 --- a/lib/splitclient-rb/helpers/util.rb +++ b/lib/splitclient-rb/helpers/util.rb @@ -17,7 +17,7 @@ def self.segment_names_in_rb_segment(object, matcher_type) names = Set.new names.merge segment_names_by_object(object, matcher_type) object[:excluded][:segments].each do |segment| - if segment[:type] == 'standard' + if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD names.add(segment[:name]) end end From a35a61c8db13d3d3e7379de0718b37757c4ec4f9 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 21 May 2025 11:57:47 -0700 Subject: [PATCH 29/44] Fix proxy error --- lib/splitclient-rb.rb | 1 + lib/splitclient-rb/engine/api/client.rb | 5 +++++ .../engine/models/split_http_response.rb | 19 +++++++++++++++++++ lib/splitclient-rb/version.rb | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 lib/splitclient-rb/engine/models/split_http_response.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index 9de3bc5e..9a3b6c5a 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -107,6 +107,7 @@ require 'splitclient-rb/engine/models/label' require 'splitclient-rb/engine/models/segment_type' require 'splitclient-rb/engine/models/treatment' +require 'splitclient-rb/engine/models/split_http_response' require 'splitclient-rb/engine/auth_api_client' require 'splitclient-rb/engine/back_off' require 'splitclient-rb/engine/push_manager' diff --git a/lib/splitclient-rb/engine/api/client.rb b/lib/splitclient-rb/engine/api/client.rb index d9d6e0df..7906443e 100644 --- a/lib/splitclient-rb/engine/api/client.rb +++ b/lib/splitclient-rb/engine/api/client.rb @@ -20,6 +20,11 @@ def get_api(url, api_key, params = {}, cache_control_headers = false) @config.split_logger.log_if_debug("GET #{url} proxy: #{api_client.proxy}") end + + rescue Zlib::GzipFile::Error => e + @config.logger.warn("Incorrect formatted response exception: #{e}\n") + SplitIoClient::Engine::Models::SplitHttpResponse.new(400, '', true) + rescue StandardError => e @config.logger.warn("#{e}\nURL:#{url}\nparams:#{params}") raise e, 'Split SDK failed to connect to backend to retrieve information', e.backtrace diff --git a/lib/splitclient-rb/engine/models/split_http_response.rb b/lib/splitclient-rb/engine/models/split_http_response.rb new file mode 100644 index 00000000..961b37f6 --- /dev/null +++ b/lib/splitclient-rb/engine/models/split_http_response.rb @@ -0,0 +1,19 @@ +module SplitIoClient + module Engine + module Models + class SplitHttpResponse + attr_accessor :status, :body + + def initialize(status, body, success) + @status = status + @body = body + @success = success + end + + def success? + @success + end + end + end + end +end diff --git a/lib/splitclient-rb/version.rb b/lib/splitclient-rb/version.rb index bc0e4bd5..0f97bfca 100644 --- a/lib/splitclient-rb/version.rb +++ b/lib/splitclient-rb/version.rb @@ -1,3 +1,3 @@ module SplitIoClient - VERSION = '8.5.0' + VERSION = '8.6.0-rc1' end From bef29ade53bd08194c26bc631961d10cae857544 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 22 May 2025 08:23:22 -0700 Subject: [PATCH 30/44] polish --- lib/splitclient-rb/engine/matchers/combining_matcher.rb | 4 +--- .../engine/matchers/rule_based_segment_matcher.rb | 7 +++++-- lib/splitclient-rb/helpers/util.rb | 6 ++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/splitclient-rb/engine/matchers/combining_matcher.rb b/lib/splitclient-rb/engine/matchers/combining_matcher.rb index b3012011..7a77d8b3 100644 --- a/lib/splitclient-rb/engine/matchers/combining_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/combining_matcher.rb @@ -57,9 +57,7 @@ def eval_and(args) @matchers.all? do |matcher| if match_with_key?(matcher) key = args[:value] - if args[:matching_key] != nil - key = args[:matching_key] - end + key = args[:matching_key] unless args[:matching_key].nil? matcher.match?(value: key) else matcher.match?(args) diff --git a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb index a1cd9d61..a5463d0a 100644 --- a/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +++ b/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb @@ -34,6 +34,7 @@ def match?(args) rule_based_segment[:conditions].each do |c| condition = SplitIoClient::Condition.new(c, @config) next if condition.empty? + matches = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository, @rule_based_segments_repository).match?(args) end @logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matches}") @@ -44,8 +45,10 @@ def match?(args) def check_excluded_segments(rule_based_segment, key, args) rule_based_segment[:excluded][:segments].each do |segment| - return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD && @segments_repository.in_segment?(segment[:name], key) - + if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD && + @segments_repository.in_segment?(segment[:name], key) + return false + end return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::RULE_BASED_SEGMENT && match_rbs( @rule_based_segments_repository.get_rule_based_segment(segment[:name]), args ) diff --git a/lib/splitclient-rb/helpers/util.rb b/lib/splitclient-rb/helpers/util.rb index 9a7de81c..070ee475 100644 --- a/lib/splitclient-rb/helpers/util.rb +++ b/lib/splitclient-rb/helpers/util.rb @@ -14,12 +14,10 @@ def self.segment_names_by_object(object, matcher_type) end def self.segment_names_in_rb_segment(object, matcher_type) - names = Set.new + names = Set.new names.merge segment_names_by_object(object, matcher_type) object[:excluded][:segments].each do |segment| - if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD - names.add(segment[:name]) - end + names.add(segment[:name]) if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD end names end From 16125cf787a2bbf9d8d5c871108ee8b8dfb5e472 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 4 Jun 2025 09:45:41 -0700 Subject: [PATCH 31/44] Added matcher --- lib/splitclient-rb.rb | 1 + .../engine/matchers/prerequisites_matcher.rb | 31 +++++++++++++++++++ .../helpers/repository_helper.rb | 12 +++++-- .../matchers/prerequisites_matcher_spec.rb | 26 ++++++++++++++++ spec/repository_helper.rb | 16 ++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb create mode 100644 spec/engine/matchers/prerequisites_matcher_spec.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index 9a3b6c5a..b4ffcdeb 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -99,6 +99,7 @@ require 'splitclient-rb/engine/matchers/between_semver_matcher' require 'splitclient-rb/engine/matchers/in_list_semver_matcher' require 'splitclient-rb/engine/matchers/rule_based_segment_matcher' +require 'splitclient-rb/engine/matchers/prerequisites_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb b/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb new file mode 100644 index 00000000..7970b63a --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module SplitIoClient + class PrerequisitesMatcher + def initialize(prerequisites, logger) + @prerequisites = prerequisites + @logger = logger + end + + def match?(args) + keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] } + + match = true + @prerequisites.each do |prerequisite| + evaluate = args[:evaluator].evaluate_feature_flag(keys, prerequisite[:n], args[:attributes]) + next if prerequisite[:ts].include?(evaluate[:treatment]) + + @logger.log_if_debug("[PrerequisitesMatcher] feature flag #{prerequisite[:n]} evaluated to #{evaluate[:treatment]} \ + that does not exist in prerequisited treatments.") + match = false + break + end + + match + end + + def string_type? + false + end + end +end diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index da72ad22..b21fabba 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -13,7 +13,7 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, next end - feature_flag = check_impressions_disabled(feature_flag, config) + feature_flag = check_missing_elements(feature_flag, config) config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled to_add.push(feature_flag) @@ -22,13 +22,21 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, feature_flag_repository.update(to_add, to_delete, change_number) end - def self.check_impressions_disabled(feature_flag, config) + def self.check_missing_elements(feature_flag, config) unless feature_flag.key?(:impressionsDisabled) feature_flag[:impressionsDisabled] = false if config.debug_enabled config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false") end end + + unless feature_flag.key?(:prerequisites) + feature_flag[:prerequisites] = [] + if config.debug_enabled + config.logger.debug("feature flag (#{feature_flag[:name]}) does not have prerequisites field, setting it to empty array") + end + end + feature_flag end diff --git a/spec/engine/matchers/prerequisites_matcher_spec.rb b/spec/engine/matchers/prerequisites_matcher_spec.rb new file mode 100644 index 00000000..fcb366c6 --- /dev/null +++ b/spec/engine/matchers/prerequisites_matcher_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::PrerequisitesMatcher do + let(:evaluator) { double } + + it 'matches with empty prerequisites' do + expect(described_class.new([], @split_logger) + .match?(matching_key: 'foo', bucketing_key: 'bar', evaluator: evaluator)).to eq(true) + end + + it 'matches with prerequisite treatments' do + allow(evaluator).to receive(:evaluate_feature_flag).with({ matching_key: 'foo', bucketing_key: 'bar' }, 'flag1', nil) + .and_return(treatment: 'on') + + expect(described_class.new([:n => 'flag1', :ts => ['on']], @split_logger) + .match?(matching_key: 'foo', bucketing_key: 'bar', evaluator: evaluator)).to eq(true) + expect(described_class.new([:n => 'flag1', :ts => ['off']], @split_logger) + .match?(matching_key: 'foo', bucketing_key: 'bar', evaluator: evaluator)).to eq(false) + end + + it 'is not string type matcher' do + expect(described_class.new([], @split_logger).string_type?).to be false + end +end diff --git a/spec/repository_helper.rb b/spec/repository_helper.rb index d38dff96..37770a76 100644 --- a/spec/repository_helper.rb +++ b/spec/repository_helper.rb @@ -83,5 +83,21 @@ expect(feature_flag_repository.get_split('split2').nil?).to eq(false) expect(feature_flag_repository.get_split('split1').nil?).to eq(true) end + + it 'test prerequisites element' do + config = SplitIoClient::SplitConfig.new(cache_adapter: :memory) + flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) + flag_sets_repository = SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([]) + feature_flag_repository = SplitIoClient::Cache::Repositories::SplitsRepository.new( + config, + flag_sets_repository, + flag_set_filter) + + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split1', :status => 'ACTIVE', conditions: [], :sets => []}], -1, config, false) + expect(feature_flag_repository.get_split('split1')[:prerequisites]).to eq([]) + + SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(feature_flag_repository, [{:name => 'split2', :status => 'ACTIVE', conditions: [], :prerequisites => [{:n => 'flag', :ts => ['on']}], :sets => []}], -1, config, false) + expect(feature_flag_repository.get_split('split2')[:prerequisites]).to eq([{:n => 'flag', :ts => ['on']}]) + end end end From e58a513569a8d55f966c63b7796f3d9731843354 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 4 Jun 2025 11:00:39 -0700 Subject: [PATCH 32/44] Updated evaluator --- lib/splitclient-rb/engine/models/label.rb | 1 + lib/splitclient-rb/engine/parser/evaluator.rb | 8 ++ spec/engine/parser/evaluator_spec.rb | 26 +++++- .../splits/engine/prerequisites_matcher.json | 79 +++++++++++++++++++ 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 spec/test_data/splits/engine/prerequisites_matcher.json diff --git a/lib/splitclient-rb/engine/models/label.rb b/lib/splitclient-rb/engine/models/label.rb index 750ad79d..d55d16b4 100644 --- a/lib/splitclient-rb/engine/models/label.rb +++ b/lib/splitclient-rb/engine/models/label.rb @@ -6,4 +6,5 @@ class SplitIoClient::Engine::Models::Label NOT_IN_SPLIT = 'not in split'.freeze NOT_READY = 'not ready'.freeze NOT_FOUND = 'definition not found'.freeze + PREREQUISITES_NOT_MET = 'prerequisites not met'.freeze end diff --git a/lib/splitclient-rb/engine/parser/evaluator.rb b/lib/splitclient-rb/engine/parser/evaluator.rb index ec7636cd..5e977632 100644 --- a/lib/splitclient-rb/engine/parser/evaluator.rb +++ b/lib/splitclient-rb/engine/parser/evaluator.rb @@ -38,6 +38,14 @@ def split_configurations(treatment, split) end def match(split, keys, attributes) + prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger) + return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless prerequisites_matcher.match?( + matching_key: keys[:matching_key], + bucketing_key: keys[:bucketing_key], + evaluator: self, + attributes: attributes + ) + in_rollout = false key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key] legacy_algo = (split[:algo] == 1 || split[:algo] == nil) ? true : false diff --git a/spec/engine/parser/evaluator_spec.rb b/spec/engine/parser/evaluator_spec.rb index a6beabe9..8aa9e2d7 100644 --- a/spec/engine/parser/evaluator_spec.rb +++ b/spec/engine/parser/evaluator_spec.rb @@ -3,12 +3,13 @@ require 'spec_helper' describe SplitIoClient::Engine::Parser::Evaluator do - let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(@default_config) } - let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(@default_config) } + let(:config) { SplitIoClient::SplitConfig.new(logger: Logger.new(StringIO.new)) } + let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])} let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} - let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(@default_config, flag_sets_repository, flag_set_filter) } - let(:evaluator) { described_class.new(segments_repository, splits_repository, rule_based_segments_repository, true) } + let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } + let(:evaluator) { described_class.new(segments_repository, splits_repository, rule_based_segments_repository, config) } let(:killed_split) { { killed: true, defaultTreatment: 'default' } } let(:archived_split) { { status: 'ARCHIVED' } } @@ -17,6 +18,11 @@ SplitIoClient.root, 'spec/test_data/splits/engine/dependency_matcher.json' )), symbolize_names: true) end + let(:split_data_prereq) do + JSON.parse(File.read(File.join( + SplitIoClient.root, 'spec/test_data/splits/engine/prerequisites_matcher.json' + )), symbolize_names: true) + end it 'returns killed treatment' do expect(evaluator.evaluate_feature_flag({ matching_key: 'foo' }, killed_split)) @@ -40,4 +46,16 @@ evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, split_data[:ff][:d][1]) end end + + context 'prerequisites matcher' do + it 'test match' do + splits_repository.update([split_data_prereq[:ff][:d][0], split_data_prereq[:ff][:d][1]], [], 1234) + + result = evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user' }, 'test_prereq') + expect(result[:treatment]).to eq('off_default') + + result = evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, 'test_prereq') + expect(result[:treatment]).to eq('on') + end + end end diff --git a/spec/test_data/splits/engine/prerequisites_matcher.json b/spec/test_data/splits/engine/prerequisites_matcher.json new file mode 100644 index 00000000..78474660 --- /dev/null +++ b/spec/test_data/splits/engine/prerequisites_matcher.json @@ -0,0 +1,79 @@ +{ "ff":{ + "d": [ + { + "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", + "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", + "name":"test_whitelist", + "prerequisites": [], + "trafficTypeId":"u", + "trafficTypeName":"User", + "seed":-1245274114, + "status":"ACTIVE", + "killed":false, + "defaultTreatment":"off", + "conditions":[ + { + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "matcherType":"WHITELIST", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "fake_user_id_1", + "fake_user_id_3" + ] + } + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":100 + } + ] + } + ], + "sets": ["set_4"] + }, + { + "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", + "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", + "name":"test_prereq", + "prerequisites": [{"n": "test_whitelist", "ts": ["on"]}], + "trafficTypeId":"u", + "trafficTypeName":"User", + "seed":-1245274114, + "status":"ACTIVE", + "killed":false, + "defaultTreatment":"off_default", + "conditions":[ + { + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "matcherType":"ALL_KEYS", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":100 + } + ] + } + ] + } + ], + "s": -1, + "t": -1 +}, "rbs": {"d":[], "s":-1, "t": -1} +} From aa3369981fc1c02551486a575556bcee68cccb82 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 4 Jun 2025 21:18:08 -0700 Subject: [PATCH 33/44] Added integration tests --- .../cache/stores/localhost_split_builder.rb | 3 +- spec/integrations/in_memory_client_spec.rb | 24 ++++++ spec/test_data/integrations/splits.json | 83 +++++++++++++++++++ .../datetime_matcher_splits.json | 1 + .../negate_number_matcher_splits.json | 1 + .../negative_number_matcher_splits.json | 1 + .../number_matcher_splits.json | 1 + .../splits/combining_matcher_splits.json | 1 + .../splits/engine/all_keys_matcher.json | 1 + .../splits/engine/configurations.json | 5 ++ .../splits/engine/dependency_matcher.json | 2 + .../splits/engine/equal_to_set_matcher.json | 1 + .../splits/engine/impressions_test.json | 2 + spec/test_data/splits/engine/killed.json | 1 + .../engine/segment_deleted_matcher.json | 1 + .../splits/engine/segment_matcher.json | 1 + .../splits/engine/segment_matcher2.json | 3 + .../splits/engine/whitelist_matcher.json | 1 + .../splits/equal_to_matcher/date_splits.json | 1 + .../equal_to_matcher/negative_splits.json | 1 + .../splits/equal_to_matcher/splits.json | 1 + .../splits/equal_to_matcher/zero_splits.json | 1 + .../date_splits.json | 1 + .../negative_splits.json | 1 + .../splits.json | 1 + spec/test_data/splits/imp-toggle.json | 3 + .../date_splits.json | 1 + .../date_splits2.json | 1 + .../negative_splits.json | 1 + .../less_than_or_equal_to_matcher/splits.json | 1 + .../semver_matchers/semver_between.json | 1 + .../semver_matchers/semver_equalto.json | 1 + .../semver_greater_or_equalto.json | 1 + .../splits/semver_matchers/semver_inlist.json | 1 + .../semver_less_or_equalto.json | 1 + spec/test_data/splits/splits.json | 2 + spec/test_data/splits/splits2.json | 3 + spec/test_data/splits/splits3.json | 2 + spec/test_data/splits/splits4.json | 1 + .../splits/splits_traffic_allocation.json | 5 ++ ...splits_traffic_allocation_one_percent.json | 1 + .../splits/whitelist_matcher_splits.json | 1 + 42 files changed, 166 insertions(+), 1 deletion(-) diff --git a/lib/splitclient-rb/cache/stores/localhost_split_builder.rb b/lib/splitclient-rb/cache/stores/localhost_split_builder.rb index b8f209ab..1fc2f949 100644 --- a/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +++ b/lib/splitclient-rb/cache/stores/localhost_split_builder.rb @@ -22,7 +22,8 @@ def build_split(feature, treatments) seed: 2_089_907_429, defaultTreatment: 'control_treatment', configurations: build_configurations(treatments), - conditions: build_conditions(treatments) + conditions: build_conditions(treatments), + prerequisites: [] } end diff --git a/spec/integrations/in_memory_client_spec.rb b/spec/integrations/in_memory_client_spec.rb index 2f2024b2..209bd3ac 100644 --- a/spec/integrations/in_memory_client_spec.rb +++ b/spec/integrations/in_memory_client_spec.rb @@ -90,6 +90,30 @@ expect(impressions[1][:treatment][:change_number]).to eq(1_506_703_262_916) end + it 'returns treatments with prereq_flag feature and check impressions' do + stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') + client.block_until_ready + expect(client.get_treatment('nico_test', 'prereq_flag')).to eq 'on' + expect(client.get_treatment('bla', 'prereq_flag')).to eq 'off_default' + + sleep 0.5 + impressions = custom_impression_listener.queue + + expect(impressions.size).to eq 2 + + expect(impressions[0][:matching_key]).to eq('nico_test') + expect(impressions[0][:split_name]).to eq('prereq_flag') + expect(impressions[0][:treatment][:treatment]).to eq('on') + expect(impressions[0][:treatment][:label]).to eq('in segment all') + expect(impressions[0][:treatment][:change_number]).to eq(1494593336752) + + expect(impressions[1][:matching_key]).to eq('bla') + expect(impressions[1][:split_name]).to eq('prereq_flag') + expect(impressions[1][:treatment][:treatment]).to eq('off_default') + expect(impressions[1][:treatment][:label]).to eq('prerequisites not met') + expect(impressions[1][:treatment][:change_number]).to eq(1494593336752) + end + it 'returns treatments with Test_Save_1 feature and check impressions' do stub_request(:get, "https://sdk.split.io/api/splitChanges?s=1.3&since=1506703262916&rbSince=-1").to_return(status: 200, body: 'ok') client.block_until_ready diff --git a/spec/test_data/integrations/splits.json b/spec/test_data/integrations/splits.json index 9881fda2..2e6054aa 100644 --- a/spec/test_data/integrations/splits.json +++ b/spec/test_data/integrations/splits.json @@ -4,6 +4,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "FACUNDO_TEST", + "prerequisites": [], "trafficAllocation": 59, "trafficAllocationSeed": -2108186082, "seed": -1947050785, @@ -124,6 +125,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testing", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 527505678, "seed": -1716462249, @@ -238,6 +240,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testing222", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -397360967, "seed": 1058132210, @@ -292,6 +295,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "a_new_split_2", + "prerequisites": [], "trafficAllocation": 99, "trafficAllocationSeed": -1349440646, "seed": -1536389703, @@ -558,6 +562,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "test_string_without_attr", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -782597068, "seed": -1682478887, @@ -628,6 +633,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "Test", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 217539832, "seed": 52164426, @@ -754,6 +760,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Test_Save_1", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -257595325, "seed": -665945237, @@ -901,6 +908,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "TEST", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -673356676, "seed": -511119211, @@ -950,6 +958,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_1", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 987354894, "seed": 1292874260, @@ -1037,6 +1046,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "nico_tests", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1409699192, "seed": -1997241870, @@ -1083,6 +1093,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo2222", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1164474086, "seed": 1270508512, @@ -1296,6 +1307,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "Tagging", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1910132597, "seed": -311493896, @@ -1345,6 +1357,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Welcome_Page_UI", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1848523960, "seed": 1608586361, @@ -1397,6 +1410,7 @@ "impressionsDisabled": false, "trafficTypeName": "test", "name": "pato_test_3", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 458647735, "seed": 95677506, @@ -1446,6 +1460,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo23", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -689658216, "seed": 1711156051, @@ -1495,6 +1510,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo909090", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1196467266, "seed": -1998101827, @@ -1662,6 +1678,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo22", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1223277820, "seed": -1152948537, @@ -1711,6 +1728,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "test-net", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -2038196969, "seed": -862203077, @@ -1760,6 +1778,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "test_dep_2", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -806171485, "seed": 922684950, @@ -1853,6 +1872,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Definition_As_Of_Clickable_UI", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -198035199, "seed": -151947071, @@ -1934,6 +1954,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Identify_UI", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -139516103, "seed": 1543172523, @@ -1983,6 +2004,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "test_definition_as_of", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1025823325, "seed": -554248124, @@ -2032,6 +2054,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "Test-jw-go", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 768122971, "seed": 1539205707, @@ -2111,6 +2134,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_7", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1340337178, "seed": -1091938685, @@ -2160,6 +2184,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_6", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1202331834, "seed": -48445256, @@ -2209,6 +2234,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_5", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 2119994290, "seed": -227092192, @@ -2258,6 +2284,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_4", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1066635158, "seed": -850704283, @@ -2307,6 +2334,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_3", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1252392550, "seed": 971538037, @@ -2356,6 +2384,7 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_2", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -285565213, "seed": -1992295819, @@ -2405,6 +2434,7 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "MAURO_TEST", + "prerequisites": [], "trafficAllocation": 59, "trafficAllocationSeed": -2108186082, "seed": -1947050766, @@ -2521,6 +2551,59 @@ "label": "in segment all" } ] + }, + { + "impressionsDisabled": false, + "trafficTypeName": "account", + "name": "prereq_flag", + "prerequisites": [ + {"n": "MAURO_TEST", "ts": ["off"]}, + {"n": "FACUNDO_TEST", "ts": ["on"]} + ], + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off_default", + "changeNumber": 1494593336752, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "in segment all" + } + ] } ], "s": -1, diff --git a/spec/test_data/splits/between_matcher/datetime_matcher_splits.json b/spec/test_data/splits/between_matcher/datetime_matcher_splits.json index 73e7ffe2..f6520c55 100644 --- a/spec/test_data/splits/between_matcher/datetime_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/datetime_matcher_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json b/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json index 9d93d964..688b0c96 100644 --- a/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json b/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json index 105ca471..6738e5ed 100644 --- a/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/between_matcher/number_matcher_splits.json b/spec/test_data/splits/between_matcher/number_matcher_splits.json index f5e98ff0..6866a2cc 100644 --- a/spec/test_data/splits/between_matcher/number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/number_matcher_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/combining_matcher_splits.json b/spec/test_data/splits/combining_matcher_splits.json index 86c258ea..72b65aaf 100644 --- a/spec/test_data/splits/combining_matcher_splits.json +++ b/spec/test_data/splits/combining_matcher_splits.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "PASSENGER_anding", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1740914610, "seed": 1288528654, diff --git a/spec/test_data/splits/engine/all_keys_matcher.json b/spec/test_data/splits/engine/all_keys_matcher.json index 70c425f8..40d57957 100644 --- a/spec/test_data/splits/engine/all_keys_matcher.json +++ b/spec/test_data/splits/engine/all_keys_matcher.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/engine/configurations.json b/spec/test_data/splits/engine/configurations.json index edaecd8d..8ec3babc 100644 --- a/spec/test_data/splits/engine/configurations.json +++ b/spec/test_data/splits/engine/configurations.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, @@ -41,6 +42,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"killed_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, @@ -77,6 +79,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"no_configs_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, @@ -109,6 +112,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"no_configs_for_treatment_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, @@ -144,6 +148,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"no_configs_killed_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/engine/dependency_matcher.json b/spec/test_data/splits/engine/dependency_matcher.json index ba4f1c4d..e64b6edb 100644 --- a/spec/test_data/splits/engine/dependency_matcher.json +++ b/spec/test_data/splits/engine/dependency_matcher.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_whitelist", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, @@ -42,6 +43,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_dependency", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/engine/equal_to_set_matcher.json b/spec/test_data/splits/engine/equal_to_set_matcher.json index 83f7cc6e..1157797c 100644 --- a/spec/test_data/splits/engine/equal_to_set_matcher.json +++ b/spec/test_data/splits/engine/equal_to_set_matcher.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "mauro_test", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1683824785, "seed": 1681260525, diff --git a/spec/test_data/splits/engine/impressions_test.json b/spec/test_data/splits/engine/impressions_test.json index 79c1524c..8033e989 100644 --- a/spec/test_data/splits/engine/impressions_test.json +++ b/spec/test_data/splits/engine/impressions_test.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"sample_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, @@ -47,6 +48,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"beta_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/engine/killed.json b/spec/test_data/splits/engine/killed.json index be6f02ba..80408f1f 100644 --- a/spec/test_data/splits/engine/killed.json +++ b/spec/test_data/splits/engine/killed.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_killed", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/engine/segment_deleted_matcher.json b/spec/test_data/splits/engine/segment_deleted_matcher.json index e7816f47..c9759d3d 100644 --- a/spec/test_data/splits/engine/segment_deleted_matcher.json +++ b/spec/test_data/splits/engine/segment_deleted_matcher.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, diff --git a/spec/test_data/splits/engine/segment_matcher.json b/spec/test_data/splits/engine/segment_matcher.json index 9c7980f1..37c46c13 100644 --- a/spec/test_data/splits/engine/segment_matcher.json +++ b/spec/test_data/splits/engine/segment_matcher.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, diff --git a/spec/test_data/splits/engine/segment_matcher2.json b/spec/test_data/splits/engine/segment_matcher2.json index d5e25dfc..8cde76b0 100644 --- a/spec/test_data/splits/engine/segment_matcher2.json +++ b/spec/test_data/splits/engine/segment_matcher2.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, @@ -43,6 +44,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature2", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, @@ -82,6 +84,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature3", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, diff --git a/spec/test_data/splits/engine/whitelist_matcher.json b/spec/test_data/splits/engine/whitelist_matcher.json index 1c491172..d513215a 100644 --- a/spec/test_data/splits/engine/whitelist_matcher.json +++ b/spec/test_data/splits/engine/whitelist_matcher.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_whitelist", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/equal_to_matcher/date_splits.json b/spec/test_data/splits/equal_to_matcher/date_splits.json index 5267517b..7e16bd32 100644 --- a/spec/test_data/splits/equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/equal_to_matcher/date_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/equal_to_matcher/negative_splits.json b/spec/test_data/splits/equal_to_matcher/negative_splits.json index 548bcee8..02df1353 100644 --- a/spec/test_data/splits/equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/equal_to_matcher/negative_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/equal_to_matcher/splits.json b/spec/test_data/splits/equal_to_matcher/splits.json index 5f603999..a2da3d4f 100644 --- a/spec/test_data/splits/equal_to_matcher/splits.json +++ b/spec/test_data/splits/equal_to_matcher/splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/equal_to_matcher/zero_splits.json b/spec/test_data/splits/equal_to_matcher/zero_splits.json index da231d90..bac8634a 100644 --- a/spec/test_data/splits/equal_to_matcher/zero_splits.json +++ b/spec/test_data/splits/equal_to_matcher/zero_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json index 643e07a9..e0f0ef91 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json index 0d5ccde7..58e82203 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json index 730aa286..2314d350 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/imp-toggle.json b/spec/test_data/splits/imp-toggle.json index b70d6684..06b04ea4 100644 --- a/spec/test_data/splits/imp-toggle.json +++ b/spec/test_data/splits/imp-toggle.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "with_track_disabled", + "prerequisites": [], "impressionsDisabled": true, "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, @@ -53,6 +54,7 @@ { "trafficTypeName": "user", "name": "with_track_enabled", + "prerequisites": [], "impressionsDisabled": false, "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, @@ -103,6 +105,7 @@ { "trafficTypeName": "user", "name": "without_track", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json index 0f74fa5b..9e23e85e 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json index eab2e7e2..d251b2b0 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json @@ -6,6 +6,7 @@ "trafficTypeId":null, "trafficTypeName":null, "name":"RUBY_isOnOrBeforeDateTimeWithAttributeValueThatDoesNotMatch", + "prerequisites": [], "seed":338948780, "status":"ACTIVE", "killed":false, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json index db1a51f5..1042dbf8 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json index cc2e6dc3..500d6bb8 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/semver_matchers/semver_between.json b/spec/test_data/splits/semver_matchers/semver_between.json index 9a0a3c7b..4a6d71af 100644 --- a/spec/test_data/splits/semver_matchers/semver_between.json +++ b/spec/test_data/splits/semver_matchers/semver_between.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "semver_between", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_equalto.json b/spec/test_data/splits/semver_matchers/semver_equalto.json index e7699e16..b292316a 100644 --- a/spec/test_data/splits/semver_matchers/semver_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_equalto.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "semver_equalto", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json index 1fde9c28..1e3d39bb 100644 --- a/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "semver_greater_or_equalto", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_inlist.json b/spec/test_data/splits/semver_matchers/semver_inlist.json index 8feafc58..fb8bd7b5 100644 --- a/spec/test_data/splits/semver_matchers/semver_inlist.json +++ b/spec/test_data/splits/semver_matchers/semver_inlist.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "semver_inlist", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json index 6704a6c3..ff809a6c 100644 --- a/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "semver_less_or_equalto", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/splits.json b/spec/test_data/splits/splits.json index 0c08e949..c2d5b3c3 100644 --- a/spec/test_data/splits/splits.json +++ b/spec/test_data/splits/splits.json @@ -3,6 +3,7 @@ { "trafficTypeName":"user", "name":"test_1_ruby", + "prerequisites": [], "seed":2089907429, "status":"ACTIVE", "killed":false, @@ -105,6 +106,7 @@ { "trafficTypeName":"user", "name":"sample_feature", + "prerequisites": [], "seed":-73470340, "status":"ACTIVE", "killed":false, diff --git a/spec/test_data/splits/splits2.json b/spec/test_data/splits/splits2.json index d688a19d..7e7d2aa7 100644 --- a/spec/test_data/splits/splits2.json +++ b/spec/test_data/splits/splits2.json @@ -3,6 +3,7 @@ { "trafficTypeName":"user", "name":"test_1_ruby", + "prerequisites": [], "seed":2089907429, "status":"ARCHIVED", "killed":false, @@ -103,6 +104,7 @@ { "trafficTypeName":"user", "name":"sample_feature", + "prerequisites": [], "seed":-73470340, "status":"ACTIVE", "killed":false, @@ -264,6 +266,7 @@ }, "rbs": {"t": 1457726098069, "s": -1, "d": [{ "changeNumber": 123, "name": "sample_rule_based_segment", + "prerequisites": [], "status": "ACTIVE", "trafficTypeName": "user", "excluded":{ diff --git a/spec/test_data/splits/splits3.json b/spec/test_data/splits/splits3.json index baae0c98..d7f3fb33 100644 --- a/spec/test_data/splits/splits3.json +++ b/spec/test_data/splits/splits3.json @@ -3,6 +3,7 @@ { "trafficTypeName":"user", "name":"test_1_ruby", + "prerequisites": [], "seed":2089907429, "status":"ACTIVE", "killed":false, @@ -105,6 +106,7 @@ { "trafficTypeName":"user", "name":"sample_feature", + "prerequisites": [], "seed":-73470340, "status":"ACTIVE", "killed":false, diff --git a/spec/test_data/splits/splits4.json b/spec/test_data/splits/splits4.json index f9d3f993..4383ac82 100644 --- a/spec/test_data/splits/splits4.json +++ b/spec/test_data/splits/splits4.json @@ -3,6 +3,7 @@ { "trafficTypeName": "user", "name": "uber_feature", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 834650759, "seed": 1165259858, diff --git a/spec/test_data/splits/splits_traffic_allocation.json b/spec/test_data/splits/splits_traffic_allocation.json index e1a58647..e3329b14 100644 --- a/spec/test_data/splits/splits_traffic_allocation.json +++ b/spec/test_data/splits/splits_traffic_allocation.json @@ -3,6 +3,7 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI", + "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1009780522, "seed": -1009780522, @@ -80,6 +81,7 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI2", + "prerequisites": [], "trafficAllocation": 90, "trafficAllocationSeed": null, "seed": -1009780522, @@ -157,6 +159,7 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI3", + "prerequisites": [], "trafficAllocation": 90, "trafficAllocationSeed": 1009780522, "seed": -1009780522, @@ -234,6 +237,7 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI4", + "prerequisites": [], "trafficAllocation": 0, "trafficAllocationSeed": null, "seed": -1009780522, @@ -311,6 +315,7 @@ { "trafficTypeName": "user", "name": "Test_on_off_on", + "prerequisites": [], "seed": 673896442, "status": "ACTIVE", "killed": false, diff --git a/spec/test_data/splits/splits_traffic_allocation_one_percent.json b/spec/test_data/splits/splits_traffic_allocation_one_percent.json index d480a521..2b842578 100644 --- a/spec/test_data/splits/splits_traffic_allocation_one_percent.json +++ b/spec/test_data/splits/splits_traffic_allocation_one_percent.json @@ -2,6 +2,7 @@ "d": [ { "name": "Traffic_Allocation_One_Percent", + "prerequisites": [], "algo": 1, "trafficAllocation": 1, "trafficAllocationSeed": -1, diff --git a/spec/test_data/splits/whitelist_matcher_splits.json b/spec/test_data/splits/whitelist_matcher_splits.json index 81259de9..5a07644d 100644 --- a/spec/test_data/splits/whitelist_matcher_splits.json +++ b/spec/test_data/splits/whitelist_matcher_splits.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, From f4ced370652dc3c969669a7624e8d8d8c0c03546 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 5 Jun 2025 13:22:12 -0700 Subject: [PATCH 34/44] polish --- lib/splitclient-rb/engine/parser/evaluator.rb | 16 +++++----- spec/test_data/integrations/splits.json | 30 ------------------- .../datetime_matcher_splits.json | 1 - .../negate_number_matcher_splits.json | 1 - .../negative_number_matcher_splits.json | 1 - .../number_matcher_splits.json | 1 - .../splits/combining_matcher_splits.json | 1 - .../splits/engine/all_keys_matcher.json | 1 - .../splits/engine/configurations.json | 4 --- .../splits/engine/dependency_matcher.json | 2 -- .../splits/engine/equal_to_set_matcher.json | 1 - .../splits/engine/impressions_test.json | 2 -- spec/test_data/splits/engine/killed.json | 1 - .../splits/engine/prerequisites_matcher.json | 1 - .../engine/segment_deleted_matcher.json | 1 - .../splits/engine/segment_matcher.json | 1 - .../splits/engine/segment_matcher2.json | 3 -- .../splits/engine/whitelist_matcher.json | 1 - .../splits/equal_to_matcher/date_splits.json | 1 - .../equal_to_matcher/negative_splits.json | 1 - .../splits/equal_to_matcher/splits.json | 1 - .../splits/equal_to_matcher/zero_splits.json | 1 - .../date_splits.json | 1 - .../negative_splits.json | 1 - .../splits.json | 1 - spec/test_data/splits/imp-toggle.json | 3 -- .../date_splits.json | 1 - .../date_splits2.json | 1 - .../negative_splits.json | 1 - .../less_than_or_equal_to_matcher/splits.json | 1 - .../semver_matchers/semver_between.json | 1 - .../semver_matchers/semver_equalto.json | 1 - .../semver_greater_or_equalto.json | 1 - .../splits/semver_matchers/semver_inlist.json | 1 - .../semver_less_or_equalto.json | 1 - spec/test_data/splits/splits.json | 2 -- spec/test_data/splits/splits2.json | 3 -- spec/test_data/splits/splits3.json | 2 -- spec/test_data/splits/splits4.json | 1 - .../splits/splits_traffic_allocation.json | 5 ---- ...splits_traffic_allocation_one_percent.json | 1 - .../splits/whitelist_matcher_splits.json | 1 - 42 files changed, 9 insertions(+), 94 deletions(-) diff --git a/lib/splitclient-rb/engine/parser/evaluator.rb b/lib/splitclient-rb/engine/parser/evaluator.rb index 5e977632..578e1651 100644 --- a/lib/splitclient-rb/engine/parser/evaluator.rb +++ b/lib/splitclient-rb/engine/parser/evaluator.rb @@ -38,13 +38,15 @@ def split_configurations(treatment, split) end def match(split, keys, attributes) - prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger) - return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless prerequisites_matcher.match?( - matching_key: keys[:matching_key], - bucketing_key: keys[:bucketing_key], - evaluator: self, - attributes: attributes - ) + if split.key?(:prerequisites) && !split[:prerequisites].nil? + prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger) + return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless prerequisites_matcher.match?( + matching_key: keys[:matching_key], + bucketing_key: keys[:bucketing_key], + evaluator: self, + attributes: attributes + ) + end in_rollout = false key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key] diff --git a/spec/test_data/integrations/splits.json b/spec/test_data/integrations/splits.json index 2e6054aa..1347a248 100644 --- a/spec/test_data/integrations/splits.json +++ b/spec/test_data/integrations/splits.json @@ -4,7 +4,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "FACUNDO_TEST", - "prerequisites": [], "trafficAllocation": 59, "trafficAllocationSeed": -2108186082, "seed": -1947050785, @@ -125,7 +124,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testing", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 527505678, "seed": -1716462249, @@ -240,7 +238,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testing222", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -397360967, "seed": 1058132210, @@ -295,7 +292,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "a_new_split_2", - "prerequisites": [], "trafficAllocation": 99, "trafficAllocationSeed": -1349440646, "seed": -1536389703, @@ -562,7 +558,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "test_string_without_attr", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -782597068, "seed": -1682478887, @@ -633,7 +628,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "Test", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 217539832, "seed": 52164426, @@ -760,7 +754,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Test_Save_1", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -257595325, "seed": -665945237, @@ -908,7 +901,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "TEST", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -673356676, "seed": -511119211, @@ -958,7 +950,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_1", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 987354894, "seed": 1292874260, @@ -1046,7 +1037,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "nico_tests", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1409699192, "seed": -1997241870, @@ -1093,7 +1083,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo2222", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1164474086, "seed": 1270508512, @@ -1307,7 +1296,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "Tagging", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1910132597, "seed": -311493896, @@ -1357,7 +1345,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Welcome_Page_UI", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1848523960, "seed": 1608586361, @@ -1410,7 +1397,6 @@ "impressionsDisabled": false, "trafficTypeName": "test", "name": "pato_test_3", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 458647735, "seed": 95677506, @@ -1460,7 +1446,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo23", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -689658216, "seed": 1711156051, @@ -1510,7 +1495,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo909090", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1196467266, "seed": -1998101827, @@ -1678,7 +1662,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "testo22", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1223277820, "seed": -1152948537, @@ -1728,7 +1711,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "test-net", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -2038196969, "seed": -862203077, @@ -1778,7 +1760,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "test_dep_2", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -806171485, "seed": 922684950, @@ -1872,7 +1853,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Definition_As_Of_Clickable_UI", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -198035199, "seed": -151947071, @@ -1954,7 +1934,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "Identify_UI", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -139516103, "seed": 1543172523, @@ -2004,7 +1983,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "test_definition_as_of", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1025823325, "seed": -554248124, @@ -2054,7 +2032,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "Test-jw-go", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 768122971, "seed": 1539205707, @@ -2134,7 +2111,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_7", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1340337178, "seed": -1091938685, @@ -2184,7 +2160,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_6", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1202331834, "seed": -48445256, @@ -2234,7 +2209,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_5", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 2119994290, "seed": -227092192, @@ -2284,7 +2258,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_4", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1066635158, "seed": -850704283, @@ -2334,7 +2307,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_3", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1252392550, "seed": 971538037, @@ -2384,7 +2356,6 @@ "impressionsDisabled": false, "trafficTypeName": "user", "name": "benchmark_jw_2", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -285565213, "seed": -1992295819, @@ -2434,7 +2405,6 @@ "impressionsDisabled": false, "trafficTypeName": "account", "name": "MAURO_TEST", - "prerequisites": [], "trafficAllocation": 59, "trafficAllocationSeed": -2108186082, "seed": -1947050766, diff --git a/spec/test_data/splits/between_matcher/datetime_matcher_splits.json b/spec/test_data/splits/between_matcher/datetime_matcher_splits.json index f6520c55..73e7ffe2 100644 --- a/spec/test_data/splits/between_matcher/datetime_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/datetime_matcher_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json b/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json index 688b0c96..9d93d964 100644 --- a/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/negate_number_matcher_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json b/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json index 6738e5ed..105ca471 100644 --- a/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/negative_number_matcher_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/between_matcher/number_matcher_splits.json b/spec/test_data/splits/between_matcher/number_matcher_splits.json index 6866a2cc..f5e98ff0 100644 --- a/spec/test_data/splits/between_matcher/number_matcher_splits.json +++ b/spec/test_data/splits/between_matcher/number_matcher_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/combining_matcher_splits.json b/spec/test_data/splits/combining_matcher_splits.json index 72b65aaf..86c258ea 100644 --- a/spec/test_data/splits/combining_matcher_splits.json +++ b/spec/test_data/splits/combining_matcher_splits.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "PASSENGER_anding", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": -1740914610, "seed": 1288528654, diff --git a/spec/test_data/splits/engine/all_keys_matcher.json b/spec/test_data/splits/engine/all_keys_matcher.json index 40d57957..70c425f8 100644 --- a/spec/test_data/splits/engine/all_keys_matcher.json +++ b/spec/test_data/splits/engine/all_keys_matcher.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/engine/configurations.json b/spec/test_data/splits/engine/configurations.json index 8ec3babc..aedf9b8f 100644 --- a/spec/test_data/splits/engine/configurations.json +++ b/spec/test_data/splits/engine/configurations.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, @@ -42,7 +41,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"killed_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, @@ -79,7 +77,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"no_configs_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, @@ -112,7 +109,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"no_configs_for_treatment_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/engine/dependency_matcher.json b/spec/test_data/splits/engine/dependency_matcher.json index e64b6edb..ba4f1c4d 100644 --- a/spec/test_data/splits/engine/dependency_matcher.json +++ b/spec/test_data/splits/engine/dependency_matcher.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_whitelist", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, @@ -43,7 +42,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_dependency", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/engine/equal_to_set_matcher.json b/spec/test_data/splits/engine/equal_to_set_matcher.json index 1157797c..83f7cc6e 100644 --- a/spec/test_data/splits/engine/equal_to_set_matcher.json +++ b/spec/test_data/splits/engine/equal_to_set_matcher.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "mauro_test", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1683824785, "seed": 1681260525, diff --git a/spec/test_data/splits/engine/impressions_test.json b/spec/test_data/splits/engine/impressions_test.json index 8033e989..79c1524c 100644 --- a/spec/test_data/splits/engine/impressions_test.json +++ b/spec/test_data/splits/engine/impressions_test.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"sample_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, @@ -48,7 +47,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"beta_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/engine/killed.json b/spec/test_data/splits/engine/killed.json index 80408f1f..be6f02ba 100644 --- a/spec/test_data/splits/engine/killed.json +++ b/spec/test_data/splits/engine/killed.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_killed", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/engine/prerequisites_matcher.json b/spec/test_data/splits/engine/prerequisites_matcher.json index 78474660..783969a8 100644 --- a/spec/test_data/splits/engine/prerequisites_matcher.json +++ b/spec/test_data/splits/engine/prerequisites_matcher.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_whitelist", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/engine/segment_deleted_matcher.json b/spec/test_data/splits/engine/segment_deleted_matcher.json index c9759d3d..e7816f47 100644 --- a/spec/test_data/splits/engine/segment_deleted_matcher.json +++ b/spec/test_data/splits/engine/segment_deleted_matcher.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, diff --git a/spec/test_data/splits/engine/segment_matcher.json b/spec/test_data/splits/engine/segment_matcher.json index 37c46c13..9c7980f1 100644 --- a/spec/test_data/splits/engine/segment_matcher.json +++ b/spec/test_data/splits/engine/segment_matcher.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, diff --git a/spec/test_data/splits/engine/segment_matcher2.json b/spec/test_data/splits/engine/segment_matcher2.json index 8cde76b0..d5e25dfc 100644 --- a/spec/test_data/splits/engine/segment_matcher2.json +++ b/spec/test_data/splits/engine/segment_matcher2.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, @@ -44,7 +43,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature2", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, @@ -84,7 +82,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"new_feature3", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1177551240, diff --git a/spec/test_data/splits/engine/whitelist_matcher.json b/spec/test_data/splits/engine/whitelist_matcher.json index d513215a..1c491172 100644 --- a/spec/test_data/splits/engine/whitelist_matcher.json +++ b/spec/test_data/splits/engine/whitelist_matcher.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_whitelist", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, diff --git a/spec/test_data/splits/equal_to_matcher/date_splits.json b/spec/test_data/splits/equal_to_matcher/date_splits.json index 7e16bd32..5267517b 100644 --- a/spec/test_data/splits/equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/equal_to_matcher/date_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/equal_to_matcher/negative_splits.json b/spec/test_data/splits/equal_to_matcher/negative_splits.json index 02df1353..548bcee8 100644 --- a/spec/test_data/splits/equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/equal_to_matcher/negative_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/equal_to_matcher/splits.json b/spec/test_data/splits/equal_to_matcher/splits.json index a2da3d4f..5f603999 100644 --- a/spec/test_data/splits/equal_to_matcher/splits.json +++ b/spec/test_data/splits/equal_to_matcher/splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/equal_to_matcher/zero_splits.json b/spec/test_data/splits/equal_to_matcher/zero_splits.json index bac8634a..da231d90 100644 --- a/spec/test_data/splits/equal_to_matcher/zero_splits.json +++ b/spec/test_data/splits/equal_to_matcher/zero_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json index e0f0ef91..643e07a9 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/date_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json index 58e82203..0d5ccde7 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/negative_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json b/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json index 2314d350..730aa286 100644 --- a/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json +++ b/spec/test_data/splits/greater_than_or_equal_to_matcher/splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/imp-toggle.json b/spec/test_data/splits/imp-toggle.json index 06b04ea4..b70d6684 100644 --- a/spec/test_data/splits/imp-toggle.json +++ b/spec/test_data/splits/imp-toggle.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "with_track_disabled", - "prerequisites": [], "impressionsDisabled": true, "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, @@ -54,7 +53,6 @@ { "trafficTypeName": "user", "name": "with_track_enabled", - "prerequisites": [], "impressionsDisabled": false, "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, @@ -105,7 +103,6 @@ { "trafficTypeName": "user", "name": "without_track", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json index 9e23e85e..0f74fa5b 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json index d251b2b0..eab2e7e2 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/date_splits2.json @@ -6,7 +6,6 @@ "trafficTypeId":null, "trafficTypeName":null, "name":"RUBY_isOnOrBeforeDateTimeWithAttributeValueThatDoesNotMatch", - "prerequisites": [], "seed":338948780, "status":"ACTIVE", "killed":false, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json index 1042dbf8..db1a51f5 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/negative_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json b/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json index 500d6bb8..cc2e6dc3 100644 --- a/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json +++ b/spec/test_data/splits/less_than_or_equal_to_matcher/splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, diff --git a/spec/test_data/splits/semver_matchers/semver_between.json b/spec/test_data/splits/semver_matchers/semver_between.json index 4a6d71af..9a0a3c7b 100644 --- a/spec/test_data/splits/semver_matchers/semver_between.json +++ b/spec/test_data/splits/semver_matchers/semver_between.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "semver_between", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_equalto.json b/spec/test_data/splits/semver_matchers/semver_equalto.json index b292316a..e7699e16 100644 --- a/spec/test_data/splits/semver_matchers/semver_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_equalto.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "semver_equalto", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json index 1e3d39bb..1fde9c28 100644 --- a/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_greater_or_equalto.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "semver_greater_or_equalto", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_inlist.json b/spec/test_data/splits/semver_matchers/semver_inlist.json index fb8bd7b5..8feafc58 100644 --- a/spec/test_data/splits/semver_matchers/semver_inlist.json +++ b/spec/test_data/splits/semver_matchers/semver_inlist.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "semver_inlist", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json index ff809a6c..6704a6c3 100644 --- a/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json +++ b/spec/test_data/splits/semver_matchers/semver_less_or_equalto.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "semver_less_or_equalto", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1068038034, "seed": -1053389887, diff --git a/spec/test_data/splits/splits.json b/spec/test_data/splits/splits.json index c2d5b3c3..0c08e949 100644 --- a/spec/test_data/splits/splits.json +++ b/spec/test_data/splits/splits.json @@ -3,7 +3,6 @@ { "trafficTypeName":"user", "name":"test_1_ruby", - "prerequisites": [], "seed":2089907429, "status":"ACTIVE", "killed":false, @@ -106,7 +105,6 @@ { "trafficTypeName":"user", "name":"sample_feature", - "prerequisites": [], "seed":-73470340, "status":"ACTIVE", "killed":false, diff --git a/spec/test_data/splits/splits2.json b/spec/test_data/splits/splits2.json index 7e7d2aa7..d688a19d 100644 --- a/spec/test_data/splits/splits2.json +++ b/spec/test_data/splits/splits2.json @@ -3,7 +3,6 @@ { "trafficTypeName":"user", "name":"test_1_ruby", - "prerequisites": [], "seed":2089907429, "status":"ARCHIVED", "killed":false, @@ -104,7 +103,6 @@ { "trafficTypeName":"user", "name":"sample_feature", - "prerequisites": [], "seed":-73470340, "status":"ACTIVE", "killed":false, @@ -266,7 +264,6 @@ }, "rbs": {"t": 1457726098069, "s": -1, "d": [{ "changeNumber": 123, "name": "sample_rule_based_segment", - "prerequisites": [], "status": "ACTIVE", "trafficTypeName": "user", "excluded":{ diff --git a/spec/test_data/splits/splits3.json b/spec/test_data/splits/splits3.json index d7f3fb33..baae0c98 100644 --- a/spec/test_data/splits/splits3.json +++ b/spec/test_data/splits/splits3.json @@ -3,7 +3,6 @@ { "trafficTypeName":"user", "name":"test_1_ruby", - "prerequisites": [], "seed":2089907429, "status":"ACTIVE", "killed":false, @@ -106,7 +105,6 @@ { "trafficTypeName":"user", "name":"sample_feature", - "prerequisites": [], "seed":-73470340, "status":"ACTIVE", "killed":false, diff --git a/spec/test_data/splits/splits4.json b/spec/test_data/splits/splits4.json index 4383ac82..f9d3f993 100644 --- a/spec/test_data/splits/splits4.json +++ b/spec/test_data/splits/splits4.json @@ -3,7 +3,6 @@ { "trafficTypeName": "user", "name": "uber_feature", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 834650759, "seed": 1165259858, diff --git a/spec/test_data/splits/splits_traffic_allocation.json b/spec/test_data/splits/splits_traffic_allocation.json index e3329b14..e1a58647 100644 --- a/spec/test_data/splits/splits_traffic_allocation.json +++ b/spec/test_data/splits/splits_traffic_allocation.json @@ -3,7 +3,6 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI", - "prerequisites": [], "trafficAllocation": 100, "trafficAllocationSeed": 1009780522, "seed": -1009780522, @@ -81,7 +80,6 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI2", - "prerequisites": [], "trafficAllocation": 90, "trafficAllocationSeed": null, "seed": -1009780522, @@ -159,7 +157,6 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI3", - "prerequisites": [], "trafficAllocation": 90, "trafficAllocationSeed": 1009780522, "seed": -1009780522, @@ -237,7 +234,6 @@ { "trafficTypeName": "account", "name": "Traffic_Allocation_UI4", - "prerequisites": [], "trafficAllocation": 0, "trafficAllocationSeed": null, "seed": -1009780522, @@ -315,7 +311,6 @@ { "trafficTypeName": "user", "name": "Test_on_off_on", - "prerequisites": [], "seed": 673896442, "status": "ACTIVE", "killed": false, diff --git a/spec/test_data/splits/splits_traffic_allocation_one_percent.json b/spec/test_data/splits/splits_traffic_allocation_one_percent.json index 2b842578..d480a521 100644 --- a/spec/test_data/splits/splits_traffic_allocation_one_percent.json +++ b/spec/test_data/splits/splits_traffic_allocation_one_percent.json @@ -2,7 +2,6 @@ "d": [ { "name": "Traffic_Allocation_One_Percent", - "prerequisites": [], "algo": 1, "trafficAllocation": 1, "trafficAllocationSeed": -1, diff --git a/spec/test_data/splits/whitelist_matcher_splits.json b/spec/test_data/splits/whitelist_matcher_splits.json index 5a07644d..81259de9 100644 --- a/spec/test_data/splits/whitelist_matcher_splits.json +++ b/spec/test_data/splits/whitelist_matcher_splits.json @@ -4,7 +4,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, From 0a8d1bdb855ef9aec46db81f6029a8501aeb0db7 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 5 Jun 2025 13:23:35 -0700 Subject: [PATCH 35/44] polish --- spec/test_data/splits/engine/configurations.json | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/test_data/splits/engine/configurations.json b/spec/test_data/splits/engine/configurations.json index aedf9b8f..edaecd8d 100644 --- a/spec/test_data/splits/engine/configurations.json +++ b/spec/test_data/splits/engine/configurations.json @@ -144,7 +144,6 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"no_configs_killed_feature", - "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-195840228, From 117a96278a6e06e7967c1767e2fd5aa55eb37cef Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany <41021307+chillaq@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:11:59 -0700 Subject: [PATCH 36/44] Revert "Updated evaluator" --- lib/splitclient-rb/engine/models/label.rb | 1 - lib/splitclient-rb/engine/parser/evaluator.rb | 8 -- spec/engine/parser/evaluator_spec.rb | 26 +----- .../splits/engine/prerequisites_matcher.json | 79 ------------------- 4 files changed, 4 insertions(+), 110 deletions(-) delete mode 100644 spec/test_data/splits/engine/prerequisites_matcher.json diff --git a/lib/splitclient-rb/engine/models/label.rb b/lib/splitclient-rb/engine/models/label.rb index d55d16b4..750ad79d 100644 --- a/lib/splitclient-rb/engine/models/label.rb +++ b/lib/splitclient-rb/engine/models/label.rb @@ -6,5 +6,4 @@ class SplitIoClient::Engine::Models::Label NOT_IN_SPLIT = 'not in split'.freeze NOT_READY = 'not ready'.freeze NOT_FOUND = 'definition not found'.freeze - PREREQUISITES_NOT_MET = 'prerequisites not met'.freeze end diff --git a/lib/splitclient-rb/engine/parser/evaluator.rb b/lib/splitclient-rb/engine/parser/evaluator.rb index 5e977632..ec7636cd 100644 --- a/lib/splitclient-rb/engine/parser/evaluator.rb +++ b/lib/splitclient-rb/engine/parser/evaluator.rb @@ -38,14 +38,6 @@ def split_configurations(treatment, split) end def match(split, keys, attributes) - prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger) - return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless prerequisites_matcher.match?( - matching_key: keys[:matching_key], - bucketing_key: keys[:bucketing_key], - evaluator: self, - attributes: attributes - ) - in_rollout = false key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key] legacy_algo = (split[:algo] == 1 || split[:algo] == nil) ? true : false diff --git a/spec/engine/parser/evaluator_spec.rb b/spec/engine/parser/evaluator_spec.rb index 8aa9e2d7..a6beabe9 100644 --- a/spec/engine/parser/evaluator_spec.rb +++ b/spec/engine/parser/evaluator_spec.rb @@ -3,13 +3,12 @@ require 'spec_helper' describe SplitIoClient::Engine::Parser::Evaluator do - let(:config) { SplitIoClient::SplitConfig.new(logger: Logger.new(StringIO.new)) } - let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(config) } - let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config) } + let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(@default_config) } + let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(@default_config) } let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])} let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} - let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) } - let(:evaluator) { described_class.new(segments_repository, splits_repository, rule_based_segments_repository, config) } + let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(@default_config, flag_sets_repository, flag_set_filter) } + let(:evaluator) { described_class.new(segments_repository, splits_repository, rule_based_segments_repository, true) } let(:killed_split) { { killed: true, defaultTreatment: 'default' } } let(:archived_split) { { status: 'ARCHIVED' } } @@ -18,11 +17,6 @@ SplitIoClient.root, 'spec/test_data/splits/engine/dependency_matcher.json' )), symbolize_names: true) end - let(:split_data_prereq) do - JSON.parse(File.read(File.join( - SplitIoClient.root, 'spec/test_data/splits/engine/prerequisites_matcher.json' - )), symbolize_names: true) - end it 'returns killed treatment' do expect(evaluator.evaluate_feature_flag({ matching_key: 'foo' }, killed_split)) @@ -46,16 +40,4 @@ evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, split_data[:ff][:d][1]) end end - - context 'prerequisites matcher' do - it 'test match' do - splits_repository.update([split_data_prereq[:ff][:d][0], split_data_prereq[:ff][:d][1]], [], 1234) - - result = evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user' }, 'test_prereq') - expect(result[:treatment]).to eq('off_default') - - result = evaluator.evaluate_feature_flag({ bucketing_key: nil, matching_key: 'fake_user_id_1' }, 'test_prereq') - expect(result[:treatment]).to eq('on') - end - end end diff --git a/spec/test_data/splits/engine/prerequisites_matcher.json b/spec/test_data/splits/engine/prerequisites_matcher.json deleted file mode 100644 index 78474660..00000000 --- a/spec/test_data/splits/engine/prerequisites_matcher.json +++ /dev/null @@ -1,79 +0,0 @@ -{ "ff":{ - "d": [ - { - "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", - "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", - "name":"test_whitelist", - "prerequisites": [], - "trafficTypeId":"u", - "trafficTypeName":"User", - "seed":-1245274114, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "conditions":[ - { - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "fake_user_id_1", - "fake_user_id_3" - ] - } - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ] - } - ], - "sets": ["set_4"] - }, - { - "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", - "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", - "name":"test_prereq", - "prerequisites": [{"n": "test_whitelist", "ts": ["on"]}], - "trafficTypeId":"u", - "trafficTypeName":"User", - "seed":-1245274114, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off_default", - "conditions":[ - { - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ] - } - ] - } - ], - "s": -1, - "t": -1 -}, "rbs": {"d":[], "s":-1, "t": -1} -} From 3c987da57b3e43de0041ad8a09cee288854c62d2 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 5 Jun 2025 14:29:44 -0700 Subject: [PATCH 37/44] polish --- spec/test_data/splits/engine/prerequisites_matcher.json | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/test_data/splits/engine/prerequisites_matcher.json b/spec/test_data/splits/engine/prerequisites_matcher.json index 783969a8..78474660 100644 --- a/spec/test_data/splits/engine/prerequisites_matcher.json +++ b/spec/test_data/splits/engine/prerequisites_matcher.json @@ -4,6 +4,7 @@ "orgId":"cee838c0-b3eb-11e5-855f-4eacec19f7bf", "environment":"cf2d09f0-b3eb-11e5-855f-4eacec19f7bf", "name":"test_whitelist", + "prerequisites": [], "trafficTypeId":"u", "trafficTypeName":"User", "seed":-1245274114, From 2a5da6197a8e0d3b669fd5b46cd8305c21113be3 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 10 Jun 2025 12:49:57 -0700 Subject: [PATCH 38/44] polish --- .../rule_based_segments_repository.rb | 26 ++++++++++--------- .../cache/repositories/splits_repository.rb | 19 ++++++++------ lib/splitclient-rb/engine/api/splits.rb | 4 +-- .../sse/workers/splits_worker.rb | 1 - 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb index 6ae16a54..79264b8f 100644 --- a/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +++ b/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb @@ -24,6 +24,9 @@ class RuleBasedSegmentsRepository < Repository }] } }] + TILL_PREFIX = '.rbsegments.till' + RB_SEGMENTS_PREFIX = '.rbsegment.' + REGISTERED_PREFIX = '.segments.registered' def initialize(config) super(config) @@ -34,8 +37,8 @@ def initialize(config) @config.cache_adapter end unless @config.mode.equal?(:consumer) - @adapter.set_string(namespace_key('.rbsegments.till'), '-1') - @adapter.initialize_map(namespace_key('.segments.registered')) + @adapter.set_string(namespace_key(TILL_PREFIX), '-1') + @adapter.initialize_map(namespace_key(REGISTERED_PREFIX)) end end @@ -46,34 +49,34 @@ def update(to_add, to_delete, new_change_number) end def get_rule_based_segment(name) - rule_based_segment = @adapter.string(namespace_key(".rbsegment.#{name}")) + rule_based_segment = @adapter.string(namespace_key("#{RB_SEGMENTS_PREFIX}#{name}")) JSON.parse(rule_based_segment, symbolize_names: true) if rule_based_segment end def rule_based_segment_names - @adapter.find_strings_by_prefix(namespace_key('.rbsegment.')) - .map { |rule_based_segment_names| rule_based_segment_names.gsub(namespace_key('.rbsegment.'), '') } + @adapter.find_strings_by_prefix(namespace_key(RB_SEGMENTS_PREFIX)) + .map { |rule_based_segment_names| rule_based_segment_names.gsub(namespace_key(RB_SEGMENTS_PREFIX), '') } end def set_change_number(since) - @adapter.set_string(namespace_key('.rbsegments.till'), since) + @adapter.set_string(namespace_key(TILL_PREFIX), since) end def get_change_number - @adapter.string(namespace_key('.rbsegments.till')) + @adapter.string(namespace_key(TILL_PREFIX)) end def set_segment_names(names) return if names.nil? || names.empty? names.each do |name| - @adapter.add_to_set(namespace_key('.segments.registered'), name) + @adapter.add_to_set(namespace_key(REGISTERED_PREFIX), name) end end def exists?(name) - @adapter.exists?(namespace_key(".rbsegment.#{name}")) + @adapter.exists?(namespace_key("#{RB_SEGMENTS_PREFIX}#{name}")) end def clear @@ -89,14 +92,13 @@ def contains?(segment_names) def add_rule_based_segment(rule_based_segment) return unless rule_based_segment[:name] - existing_rule_based_segment = get_rule_based_segment(rule_based_segment[:name]) if check_undefined_matcher(rule_based_segment) @config.logger.warn("Rule based segment #{rule_based_segment[:name]} has undefined matcher, setting conditions to default template.") rule_based_segment[:conditions] = RuleBasedSegmentsRepository::DEFAULT_CONDITIONS_TEMPLATE end - @adapter.set_string(namespace_key(".rbsegment.#{rule_based_segment[:name]}"), rule_based_segment.to_json) + @adapter.set_string(namespace_key("#{RB_SEGMENTS_PREFIX}#{rule_based_segment[:name]}"), rule_based_segment.to_json) end def check_undefined_matcher(rule_based_segment) @@ -112,7 +114,7 @@ def check_undefined_matcher(rule_based_segment) end def remove_rule_based_segment(rule_based_segment) - @adapter.delete(namespace_key(".rbsegment.#{rule_based_segment[:name]}")) + @adapter.delete(namespace_key("#{RB_SEGMENTS_PREFIX}#{rule_based_segment[:name]}")) end end end diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index 2fa7c396..c5fbcd2c 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -31,6 +31,9 @@ class SplitsRepository < Repository ], label: "targeting rule type unsupported by sdk" }] + TILL_PREFIX = '.splits.till' + SPLIT_PREFIX = '.splits.' + READY_PREFIX = '.splits.ready' def initialize(config, flag_sets_repository, flag_set_filter) super(config) @@ -84,16 +87,16 @@ def traffic_type_exists(tt_name) # Return an array of Split Names excluding control keys like splits.till def split_names - @adapter.find_strings_by_prefix(namespace_key('.split.')) - .map { |split| split.gsub(namespace_key('.split.'), '') } + @adapter.find_strings_by_prefix(namespace_key(SPLIT_PREFIX)) + .map { |split| split.gsub(namespace_key(SPLIT_PREFIX), '') } end def set_change_number(since) - @adapter.set_string(namespace_key('.splits.till'), since) + @adapter.set_string(namespace_key(TILL_PREFIX), since) end def get_change_number - @adapter.string(namespace_key('.splits.till')) + @adapter.string(namespace_key(TILL_PREFIX)) end def set_segment_names(names) @@ -109,15 +112,15 @@ def exists?(name) end def ready? - @adapter.string(namespace_key('.splits.ready')).to_i != -1 + @adapter.string(namespace_key(READY_PREFIX)).to_i != -1 end def not_ready! - @adapter.set_string(namespace_key('.splits.ready'), -1) + @adapter.set_string(namespace_key(READY_PREFIX), -1) end def ready! - @adapter.set_string(namespace_key('.splits.ready'), Time.now.utc.to_i) + @adapter.set_string(namespace_key(READY_PREFIX), Time.now.utc.to_i) end def clear @@ -167,7 +170,7 @@ def flag_set_filter def initialize_keys unless @config.mode.equal?(:consumer) - @adapter.set_string(namespace_key('.splits.till'), '-1') + @adapter.set_string(namespace_key(TILL_PREFIX), '-1') @adapter.initialize_map(namespace_key('.segments.registered')) end end diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index e00a76a0..7e8c3564 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -60,7 +60,7 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till if response.success? result = JSON.parse(response.body, symbolize_names: true) if @spec_version == Splits::SPEC_1_1 - result = convert_to_newSPEC(result) + result = convert_to_new_spec(result) end result[:rbs][:d] = check_rbs_data(result[:rbs][:d]) @@ -129,7 +129,7 @@ def check_last_proxy_check_timestamp @spec_version == Splits::SPEC_1_1 and ((Time.now - @last_proxy_check_timestamp) >= Splits::PROXY_CHECK_INTERVAL_SECONDS) end - def convert_to_newSPEC(body) + def convert_to_new_spec(body) {:ff => {:d => body[:splits], :s => body[:since], :t => body[:till]}, :rbs => {:d => [], :s => -1, :t => -1}} end end diff --git a/lib/splitclient-rb/sse/workers/splits_worker.rb b/lib/splitclient-rb/sse/workers/splits_worker.rb index 4fe7563e..dc15eb2c 100644 --- a/lib/splitclient-rb/sse/workers/splits_worker.rb +++ b/lib/splitclient-rb/sse/workers/splits_worker.rb @@ -105,7 +105,6 @@ def update_rule_based_segment(notification) fetch_segments_if_not_exists(Helpers::Util.segment_names_in_rb_segment(new_rb_segment, 'IN_SEGMENT'), @rule_based_segment_repository) - # TODO: enable when telemetry spec is added # @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS) true From f8a3c465e75b5ab28ba296c01c2ae01022ba4d76 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 10 Jun 2025 13:03:38 -0700 Subject: [PATCH 39/44] polish --- lib/splitclient-rb/cache/repositories/splits_repository.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index c5fbcd2c..9fcae0e0 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -32,7 +32,7 @@ class SplitsRepository < Repository label: "targeting rule type unsupported by sdk" }] TILL_PREFIX = '.splits.till' - SPLIT_PREFIX = '.splits.' + SPLIT_PREFIX = '.split.' READY_PREFIX = '.splits.ready' def initialize(config, flag_sets_repository, flag_set_filter) From e728d3e290b03a174a71ffcaf5da3222a491ece0 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 11 Jun 2025 09:08:27 -0700 Subject: [PATCH 40/44] Added missing label --- lib/splitclient-rb/engine/models/label.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/splitclient-rb/engine/models/label.rb b/lib/splitclient-rb/engine/models/label.rb index 750ad79d..d55d16b4 100644 --- a/lib/splitclient-rb/engine/models/label.rb +++ b/lib/splitclient-rb/engine/models/label.rb @@ -6,4 +6,5 @@ class SplitIoClient::Engine::Models::Label NOT_IN_SPLIT = 'not in split'.freeze NOT_READY = 'not ready'.freeze NOT_FOUND = 'definition not found'.freeze + PREREQUISITES_NOT_MET = 'prerequisites not met'.freeze end From fc69af4cb3aed40a78dcc712852f62dab6f602ff Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 11 Jun 2025 09:23:14 -0700 Subject: [PATCH 41/44] updated changes and version --- CHANGES.txt | 4 ++++ lib/splitclient-rb/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2ad00a32..55f01c4e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,9 @@ CHANGES +8.6.0 (Jun xx, 2025) +- Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK. +- Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules. + 8.5.0 (Jan 17, 2025) - Fixed high cpu usage when unique keys are cleared every 24 hours. - Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on SplitView type objects. Read more in our docs. diff --git a/lib/splitclient-rb/version.rb b/lib/splitclient-rb/version.rb index 0f97bfca..d0431b85 100644 --- a/lib/splitclient-rb/version.rb +++ b/lib/splitclient-rb/version.rb @@ -1,3 +1,3 @@ module SplitIoClient - VERSION = '8.6.0-rc1' + VERSION = '8.6.0' end From 4d4c830b5be6c697ade83d71d8ae413961e9e38f Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 11 Jun 2025 10:36:46 -0700 Subject: [PATCH 42/44] polish --- lib/splitclient-rb/engine/api/splits.rb | 72 ++++++++++--------- lib/splitclient-rb/engine/parser/evaluator.rb | 41 +++++++---- lib/splitclient-rb/engine/synchronizer.rb | 16 +++-- 3 files changed, 74 insertions(+), 55 deletions(-) diff --git a/lib/splitclient-rb/engine/api/splits.rb b/lib/splitclient-rb/engine/api/splits.rb index 7e8c3564..887adb26 100644 --- a/lib/splitclient-rb/engine/api/splits.rb +++ b/lib/splitclient-rb/engine/api/splits.rb @@ -43,7 +43,6 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till params[:till] = fetch_options[:till] unless fetch_options[:till].nil? @config.logger.debug("Fetching from splitChanges with #{params}: ") response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers]) - if response.status == 414 @config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.") raise ApiException.new response.body, 414 @@ -56,44 +55,17 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till return since(since, 0, fetch_options = {cache_control_headers: fetch_options[:cache_control_headers], till: fetch_options[:till], sets: fetch_options[:sets]}) end - if response.success? result = JSON.parse(response.body, symbolize_names: true) - if @spec_version == Splits::SPEC_1_1 - result = convert_to_new_spec(result) - end - - result[:rbs][:d] = check_rbs_data(result[:rbs][:d]) - result = objects_with_segment_names(result) - - if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION - @clear_storage = @last_proxy_check_timestamp != 0 - @last_proxy_check_timestamp = 0 - end - - unless result[:ff][:d].empty? - @config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}") - end - @config.split_logger.log_if_transport("Feature flag changes response: #{result[:ff].to_s}") - - unless result[:rbs][:d].empty? - @config.split_logger.log_if_debug("#{result[:rbs][:d].length} rule based segments retrieved. since=#{since_rbs}") - end - @config.split_logger.log_if_transport("rule based segments changes response: #{result[:rbs].to_s}") - - bucket = BinarySearchLatencyTracker.get_bucket((Time.now - start) * 1000.0) - @telemetry_runtime_producer.record_sync_latency(Telemetry::Domain::Constants::SPLIT_SYNC, bucket) - @telemetry_runtime_producer.record_successful_sync(Telemetry::Domain::Constants::SPLIT_SYNC, (Time.now.to_f * 1000.0).to_i) - - result - else - @telemetry_runtime_producer.record_sync_error(Telemetry::Domain::Constants::SPLIT_SYNC, response.status) - @config.logger.error("Unexpected status code while fetching feature flags: #{response.status}. " \ - 'Check your API key and base URI') - - raise 'Split SDK failed to connect to backend to fetch feature flags definitions' + return process_result(result, since, since_rbs, start) end + @telemetry_runtime_producer.record_sync_error(Telemetry::Domain::Constants::SPLIT_SYNC, response.status) + + @config.logger.error("Unexpected status code while fetching feature flags: #{response.status}. " \ + 'Check your API key and base URI') + + raise 'Split SDK failed to connect to backend to fetch feature flags definitions' end def clear_storage @@ -102,6 +74,36 @@ def clear_storage private + def process_result(result, since, since_rbs, start) + if @spec_version == Splits::SPEC_1_1 + result = convert_to_new_spec(result) + end + + result[:rbs][:d] = check_rbs_data(result[:rbs][:d]) + result = objects_with_segment_names(result) + + if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION + @clear_storage = @last_proxy_check_timestamp != 0 + @last_proxy_check_timestamp = 0 + end + + unless result[:ff][:d].empty? + @config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}") + end + @config.split_logger.log_if_transport("Feature flag changes response: #{result[:ff].to_s}") + + unless result[:rbs][:d].empty? + @config.split_logger.log_if_debug("#{result[:rbs][:d].length} rule based segments retrieved. since=#{since_rbs}") + end + @config.split_logger.log_if_transport("rule based segments changes response: #{result[:rbs].to_s}") + + bucket = BinarySearchLatencyTracker.get_bucket((Time.now - start) * 1000.0) + @telemetry_runtime_producer.record_sync_latency(Telemetry::Domain::Constants::SPLIT_SYNC, bucket) + @telemetry_runtime_producer.record_successful_sync(Telemetry::Domain::Constants::SPLIT_SYNC, (Time.now.to_f * 1000.0).to_i) + + result + end + def check_rbs_data(rbs_data) rbs_data.each do |rb_segment| rb_segment[:excluded] = {:keys => [], :segments => []} if rb_segment[:excluded].nil? diff --git a/lib/splitclient-rb/engine/parser/evaluator.rb b/lib/splitclient-rb/engine/parser/evaluator.rb index 578e1651..49e850a6 100644 --- a/lib/splitclient-rb/engine/parser/evaluator.rb +++ b/lib/splitclient-rb/engine/parser/evaluator.rb @@ -38,15 +38,8 @@ def split_configurations(treatment, split) end def match(split, keys, attributes) - if split.key?(:prerequisites) && !split[:prerequisites].nil? - prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger) - return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless prerequisites_matcher.match?( - matching_key: keys[:matching_key], - bucketing_key: keys[:bucketing_key], - evaluator: self, - attributes: attributes - ) - end + + return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless check_prerequisites_matcher(split, keys, attributes) in_rollout = false key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key] @@ -80,19 +73,39 @@ def match(split, keys, attributes) result = splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo]) - if result.nil? - return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) - else - return treatment_hash(c[:label], result, split[:changeNumber],split_configurations(result, split)) - end + return treatment_from_result(result, split, c) end treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) end + private + + def treatment_from_result(result, split, condition) + if result.nil? + return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) + else + return treatment_hash(condition[:label], result, split[:changeNumber],split_configurations(result, split)) + end + end + def treatment_hash(label, treatment, change_number = nil, configurations = nil) { label: label, treatment: treatment, change_number: change_number, config: configurations } end + + def check_prerequisites_matcher(split, keys, attributes) + if split.key?(:prerequisites) && !split[:prerequisites].nil? + prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger) + return prerequisites_matcher.match?( + matching_key: keys[:matching_key], + bucketing_key: keys[:bucketing_key], + evaluator: self, + attributes: attributes + ) + end + + true + end end end end diff --git a/lib/splitclient-rb/engine/synchronizer.rb b/lib/splitclient-rb/engine/synchronizer.rb index 99461670..841aff6d 100644 --- a/lib/splitclient-rb/engine/synchronizer.rb +++ b/lib/splitclient-rb/engine/synchronizer.rb @@ -97,12 +97,7 @@ def fetch_splits(target_change_number, rbs_target_change_number) attempts = ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES - result[:remaining_attempts] - if result[:success] - @segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty? - @config.logger.debug("Refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled - else - @config.logger.debug("No changes fetched after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled - end + process_result(result, attempts) rescue StandardError => e @config.log_found_exception(__method__.to_s, e) end @@ -145,6 +140,15 @@ def fetch_segment(name, target_change_number) private + def process_result(result, attempts) + if result[:success] + @segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty? + @config.logger.debug("Refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled + else + @config.logger.debug("No changes fetched after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled + end + end + def attempt_segment_sync(name, target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff) remaining_attempts = max_retries @segments_sync_backoff.reset From 31a145d3af66676f37856ce4865810d312ca3a77 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany <41021307+chillaq@users.noreply.github.com> Date: Mon, 16 Jun 2025 08:42:12 -0700 Subject: [PATCH 43/44] Update CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 55f01c4e..e2616a89 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,6 @@ CHANGES -8.6.0 (Jun xx, 2025) +8.6.0 (Jun 16, 2025) - Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK. - Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules. From 3fd624e3706ce94986349d875503214436aa5fcf Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany <41021307+chillaq@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:11:45 -0700 Subject: [PATCH 44/44] Update CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index e2616a89..33219edd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,6 @@ CHANGES -8.6.0 (Jun 16, 2025) +8.6.0 (Jun 17, 2025) - Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK. - Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules.