diff --git a/lib/splitclient-rb/cache/repositories/flag_sets_repository.rb b/lib/splitclient-rb/cache/repositories/flag_sets_repository.rb index 4baf48d2..4ebea579 100644 --- a/lib/splitclient-rb/cache/repositories/flag_sets_repository.rb +++ b/lib/splitclient-rb/cache/repositories/flag_sets_repository.rb @@ -3,7 +3,7 @@ module SplitIoClient module Cache module Repositories - class FlagSets + class FlagSetsRepository def initialize(flag_sets = []) @sets_feature_flag_map = {} flag_sets.each{ |flag_set| @sets_feature_flag_map[flag_set] = Set[] } diff --git a/lib/splitclient-rb/cache/repositories/splits_repository.rb b/lib/splitclient-rb/cache/repositories/splits_repository.rb index 378174bc..377b9c15 100644 --- a/lib/splitclient-rb/cache/repositories/splits_repository.rb +++ b/lib/splitclient-rb/cache/repositories/splits_repository.rb @@ -6,7 +6,7 @@ module Repositories class SplitsRepository < Repository attr_reader :adapter - def initialize(config, flag_sets = []) + def initialize(config, flag_sets_repository, flag_set_filter) super(config) @tt_cache = {} @adapter = case @config.cache_adapter.class.to_s @@ -15,8 +15,8 @@ def initialize(config, flag_sets = []) else @config.cache_adapter end - @flag_sets = SplitIoClient::Cache::Repositories::FlagSets.new(flag_sets) - @flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new(flag_sets) + @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')) diff --git a/lib/splitclient-rb/helpers/repository_helper.rb b/lib/splitclient-rb/helpers/repository_helper.rb index 192d18cc..d7294c45 100644 --- a/lib/splitclient-rb/helpers/repository_helper.rb +++ b/lib/splitclient-rb/helpers/repository_helper.rb @@ -7,15 +7,14 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, to_add = [] to_delete = [] for feature_flag in feature_flags - if feature_flag_repository.flag_set_filter.intersect?(feature_flag[:sets]) && !Engine::Models::Split.archived?(feature_flag) - to_add.push(feature_flag) - config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled - else - if !feature_flag_repository.get_split(feature_flag[:name]).nil? - config.logger.debug("removing feature flag from store(#{feature_flag})") if config.debug_enabled - to_delete.push(feature_flag) - end + if Engine::Models::Split.archived?(feature_flag) || !feature_flag_repository.flag_set_filter.intersect?(feature_flag[:sets]) + config.logger.debug("removing feature flag from store(#{feature_flag})") if config.debug_enabled + to_delete.push(feature_flag) + next end + + config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled + to_add.push(feature_flag) end feature_flag_repository.update(to_add, to_delete, change_number) end diff --git a/lib/splitclient-rb/split_factory.rb b/lib/splitclient-rb/split_factory.rb index 4a18f4fa..1eeb7e39 100644 --- a/lib/splitclient-rb/split_factory.rb +++ b/lib/splitclient-rb/split_factory.rb @@ -35,6 +35,7 @@ def initialize(api_key, config_hash = {}) register_factory build_telemetry_components + build_flag_sets_filter build_repositories build_telemetry_synchronizer build_impressions_sender_adapter @@ -55,11 +56,11 @@ def start! if @config.consumer? build_synchronizer build_sync_manager - + @sync_manager.start_consumer return end - + build_fetchers build_synchronizer build_streaming_components @@ -135,7 +136,7 @@ def validate_api_key end def repositories - { + { splits: @splits_repository, segments: @segments_repository, impressions: @impressions_repository, @@ -188,7 +189,7 @@ def build_streaming_components 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) - event_parser = SSE::EventSource::EventParser.new(config) + event_parser = SSE::EventSource::EventParser.new(config) sse_client = SSE::EventSource::Client.new(@config, @api_key, @runtime_producer, event_parser, notification_manager_keeper, notification_processor, @push_status_queue) @sse_handler = SSE::SSEHandler.new(@config, splits_worker, segments_worker, sse_client) @push_manager = Engine::PushManager.new(@config, @sse_handler, @api_key, @runtime_producer) @@ -199,7 +200,8 @@ def build_sync_manager end def build_repositories - @splits_repository = SplitsRepository.new(@config) + @flag_sets_repository = SplitIoClient::Cache::Repositories::FlagSetsRepository.new(@config.flag_sets_filter) + @splits_repository = SplitsRepository.new(@config, @flag_sets_repository, @flag_sets_filter) @segments_repository = SegmentsRepository.new(@config) @impressions_repository = ImpressionsRepository.new(@config) @events_repository = EventsRepository.new(@config, @api_key, @runtime_producer) @@ -251,5 +253,9 @@ def build_impressions_components @impressions_manager = Engine::Common::ImpressionManager.new(@config, @impressions_repository, @impression_counter, @runtime_producer, @impression_observer, @unique_keys_tracker) end + + def build_flag_sets_filter + @flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new(@config.flag_sets_filter) + end end end diff --git a/spec/cache/repositories/flag_set_repository_spec.rb b/spec/cache/repositories/flag_set_repository_spec.rb new file mode 100644 index 00000000..c6a1355f --- /dev/null +++ b/spec/cache/repositories/flag_set_repository_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'set' + +describe SplitIoClient::Cache::Repositories::FlagSetsRepository do + context 'test flag set repository' do + it 'test add/delete' do + flag_set = SplitIoClient::Cache::Repositories::FlagSetsRepository.new [] + expect(flag_set.instance_variable_get(:@sets_feature_flag_map)).to eq({}) + + flag_set.add_flag_set('set_1') + expect(flag_set.flag_set_exist?('set_1')).to eq(true) + expect(flag_set.get_flag_set('set_1')).to eq(Set[]) + + flag_set.add_flag_set('set_2') + expect(flag_set.flag_set_exist?('set_2')).to eq(true) + expect(flag_set.get_flag_set('set_2')).to eq(Set[]) + + flag_set.remove_flag_set('set_1') + expect(flag_set.flag_set_exist?('set_1')).to eq(false) + + flag_set.remove_flag_set('set_2') + expect(flag_set.instance_variable_get(:@sets_feature_flag_map)).to eq({}) + end + + it 'test add/delete feature flags to flag sets' do + flag_set = SplitIoClient::Cache::Repositories::FlagSetsRepository.new [] + expect(flag_set.instance_variable_get(:@sets_feature_flag_map)).to eq({}) + + flag_set.add_flag_set('set_1') + flag_set.add_feature_flag_to_flag_set('set_1', 'feature1') + expect(flag_set.flag_set_exist?('set_1')).to eq(true) + expect(flag_set.get_flag_set('set_1')).to eq(Set['feature1']) + + flag_set.add_feature_flag_to_flag_set('set_1', 'feature2') + expect(flag_set.get_flag_set('set_1')).to eq(Set['feature1', 'feature2']) + + flag_set.remove_feature_flag_from_flag_set('set_1', 'feature1') + expect(flag_set.get_flag_set('set_1')).to eq(Set['feature2']) + + flag_set.remove_feature_flag_from_flag_set('set_1', 'feature2') + expect(flag_set.get_flag_set('set_1')).to eq(Set[]) + + end + end +end diff --git a/spec/cache/repositories/splits_repository_spec.rb b/spec/cache/repositories/splits_repository_spec.rb index cebd3f97..5383fc05 100644 --- a/spec/cache/repositories/splits_repository_spec.rb +++ b/spec/cache/repositories/splits_repository_spec.rb @@ -6,7 +6,9 @@ describe SplitIoClient::Cache::Repositories::SplitsRepository do RSpec.shared_examples 'Splits Repository' do |cache_adapter| let(:config) { SplitIoClient::SplitConfig.new(cache_adapter: cache_adapter) } - let(:repository) { described_class.new(config) } + let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::FlagSetsRepository.new([])} + let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])} + let(:repository) { described_class.new(config, flag_sets_repository, flag_set_filter) } before :all do redis = Redis.new @@ -94,7 +96,7 @@ end it 'returns splits data' do - expect(repository.splits).to eq( + expect(repository.splits(repository.split_names)).to eq( 'foo' => { name: 'foo', trafficTypeName: 'tt_name_1' }, 'bar' => { name: 'bar', trafficTypeName: 'tt_name_2' }, 'baz' => { name: 'baz', trafficTypeName: 'tt_name_1' } diff --git a/spec/filter/flag_set_filter_spec.rb b/spec/filter/flag_set_filter_spec.rb new file mode 100644 index 00000000..4368efe3 --- /dev/null +++ b/spec/filter/flag_set_filter_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe SplitIoClient::Cache::Filter::FlagSetsFilter do + subject { SplitIoClient::Cache::Filter::FlagSetsFilter } + + it 'validate initialize, contains one or multiple sets' do + fs = subject.new(['set_1', 'set_2']) + + expect(fs.flag_set_exist?('set_1')).to eq(true) + expect(fs.flag_set_exist?('set_3')).to eq(false) + expect(fs.flag_set_exist?(1)).to eq(false) + + expect(fs.intersect?(['set_3', 'set_1'])).to eq(true) + expect(fs.intersect?(['set_2', 'set_1'])).to eq(true) + expect(fs.intersect?(['set_3', 'set_4'])).to eq(false) + expect(fs.intersect?('set_1')).to eq(false) + + fs2 = subject.new() + expect(fs2.flag_set_exist?('set_1')).to eq(true) + expect(fs2.intersect?(['set_2', 'set_1'])).to eq(true) + + end +end diff --git a/spec/repository_helper.rb b/spec/repository_helper.rb new file mode 100644 index 00000000..e6abf0db --- /dev/null +++ b/spec/repository_helper.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true +require 'spec_helper' +require 'set' + +describe SplitIoClient::Helpers::RepositoryHelper do + context 'test repository helper' do + it 'with flag set filter' do + config = SplitIoClient::SplitConfig.new(cache_adapter: :memory) + flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new(['set_1', 'set_2']) + flag_sets_repository = SplitIoClient::Cache::Repositories::FlagSetsRepository.new(['set_1', 'set_2']) + 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', :sets => []}], -1, config) + 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', :sets => ['set_3']}], -1, config) + 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', :sets => ['set_1']}], -1, config) + 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', :sets => ['set_1']}], -1, config) + expect(feature_flag_repository.get_split('split1').nil?).to eq(true) + end + + it 'without flag set filter' do + config = SplitIoClient::SplitConfig.new(cache_adapter: :memory) + flag_set_filter = SplitIoClient::Cache::Filter::FlagSetsFilter.new([]) + flag_sets_repository = SplitIoClient::Cache::Repositories::FlagSetsRepository.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', :sets => []}], -1, config) + 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', :sets => ['set_3']}], -1, config) + 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', :sets => ['set_1']}], -1, config) + 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', :sets => ['set_1']}], -1, config) + expect(feature_flag_repository.get_split('split1').nil?).to eq(true) + end + end +end