From 6f98c7303a256dc0b1a3886de789752ec8be06b3 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Fri, 21 Jul 2023 12:10:11 +0200 Subject: [PATCH] Provide filter_results to refine a search rspec-puppet-facts has a pattern where it does a lot of small searches for every OS. By doing a single complex FacterDB.get_facts call to get the only supported operating systems and then further refining that result it can use the cache provided by FacterDB.get_facts. This is quite hacky now because it duplicates internals from JGrep to avoid serializing to JSON and parsing that again. --- lib/facterdb.rb | 18 ++++++++++++++++++ spec/facter_db_spec.rb | 18 ++++++++++++++++++ spec/spec_helper.rb | 1 + 3 files changed, 37 insertions(+) diff --git a/lib/facterdb.rb b/lib/facterdb.rb index 6370ba79..1974faae 100644 --- a/lib/facterdb.rb +++ b/lib/facterdb.rb @@ -123,4 +123,22 @@ def self.get_facts(filter = nil, cache = true, symbolize_keys: true) end result end + + # @param results [Array[Hash]] The results from a get_facts call + # @param filter [Object] The filter to convert to jgrep string + # @return [Array[Hash[Symbol, Any]]] + # Array of hashes of facts when symbolize_keys is true + # @return [Array[Hash[String, Any]]] + # Array of hashes of facts when symbolize_keys is false + def self.filter_results(results, filter, symbolize_keys: true) + database = symbolize_keys ? results.map { |hash| hash.transform_keys(&:to_s) } : results + + # TODO: This duplicates JGrep.jgrep to avoid JSON.parse(database.to_json) + # JGrep should provide an API for that + expression = generate_filter_str(filter) + call_stack = JGrep::Parser.new(expression).execution_stack + filtered = database.filter { |document| JGrep.eval_statement(document, call_stack) } + + symbolize_keys ? filtered.map { |hash| hash.transform_keys(&:to_sym) } : filtered + end end diff --git a/spec/facter_db_spec.rb b/spec/facter_db_spec.rb index 965d389f..c9c10f1f 100644 --- a/spec/facter_db_spec.rb +++ b/spec/facter_db_spec.rb @@ -289,4 +289,22 @@ end end end + + describe '.filter_results' do + subject(:filtered) { FacterDB.filter_results(results, filter) } + + let(:filters) { 'osfamily=Debian or osfamily=RedHat' } + let(:results) { FacterDB.get_facts(filters) } + + # Just to make sure our results set contains what we want + it 'has both Debian and Red Hat in the unfiltered result' do + expect(results).to include(hash_including(osfamily: 'Debian')).and include(hash_including(osfamily: 'RedHat')) + end + + context 'with filter for Debian' do + let(:filter) { 'osfamily=Debian' } + + it { is_expected.to include(hash_including(osfamily: 'Debian')).and not_include(hash_including(osfamily: 'RedHat')) } + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9d2ee2bd..71429fbb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,6 +14,7 @@ RSpec::Matchers.define_negated_matcher :not_be_nil, :be_nil RSpec::Matchers.define_negated_matcher :not_be_empty, :be_empty +RSpec::Matchers.define_negated_matcher :not_include, :include def project_dir File.dirname File.dirname(File.expand_path(__FILE__))