From ea9928adf0503d57581d1469298d013d49e562e2 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 7 May 2025 18:53:15 -0700 Subject: [PATCH 1/2] 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 2/2] 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]))