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