diff --git a/lib/miq_expression.rb b/lib/miq_expression.rb index 4a19cee8100..8f7ca68d226 100644 --- a/lib/miq_expression.rb +++ b/lib/miq_expression.rb @@ -43,6 +43,16 @@ def valid?(component = exp) end end + def set_tagged_target(model, associations = []) + each_atom(exp) do |atom| + next unless atom.key?("tag") + tag = Tag.parse(atom["tag"]) + tag.model = model + tag.associations = associations + atom["tag"] = tag.to_s + end + end + def self.proto? return @proto if defined?(@proto) @proto = ::Settings.product.proto @@ -1436,4 +1446,19 @@ def self.determine_relat_path(ref) last_path end private_class_method :determine_relat_path + + def each_atom(component, &block) + operator = component.keys.first + + case operator.downcase + when "and", "or" + component[operator].each { |sub_component| each_atom(sub_component, &block) } + when "not", "!" + each_atom(component[operator], &block) + when "find" + component[operator].each { |_operator, operands| each_atom(operands, &block) } + else + yield(component[operator]) + end + end end # class MiqExpression diff --git a/lib/miq_expression/field.rb b/lib/miq_expression/field.rb index 81bd4336720..6745f29c6e5 100644 --- a/lib/miq_expression/field.rb +++ b/lib/miq_expression/field.rb @@ -12,6 +12,7 @@ class MiqExpression::Field < MiqExpression::Target def self.parse(field) parsed_params = parse_params(field) || return + return unless parsed_params[:model_name] new(parsed_params[:model_name], parsed_params[:associations], parsed_params[:column] || parsed_params[:virtual_custom_column]) end diff --git a/lib/miq_expression/tag.rb b/lib/miq_expression/tag.rb index 161483108ce..758d1862c8a 100644 --- a/lib/miq_expression/tag.rb +++ b/lib/miq_expression/tag.rb @@ -1,7 +1,7 @@ class MiqExpression::Tag < MiqExpression::Target REGEX = / -(?([[:alnum:]]*(::)?)+) -\.(?([a-z_]+\.)*) +(?([[:alnum:]]*(::)?)?) +\.?(?([a-z_]+\.)*) (?\bmanaged|user_tag\b) -(?[a-z]+[_[:alnum:]]+) /x diff --git a/lib/miq_expression/target.rb b/lib/miq_expression/target.rb index 77a2f24a413..557f915a227 100644 --- a/lib/miq_expression/target.rb +++ b/lib/miq_expression/target.rb @@ -13,12 +13,13 @@ def self.parse_params(field) # convert matches to hash to format # {:model_name => 'User', :associations => ...} parsed_params = Hash[match.names.map(&:to_sym).zip(match.to_a[1..-1])] - parsed_params[:model_name] = parsed_params[:model_name].classify.safe_constantize || return + parsed_params[:model_name] = parsed_params[:model_name].classify.safe_constantize parsed_params[:associations] = parsed_params[:associations].to_s.split(".") parsed_params end - attr_reader :model, :associations, :column + attr_reader :column + attr_accessor :model, :associations def initialize(model, associations, column) @model = model diff --git a/spec/lib/miq_expression/tag_spec.rb b/spec/lib/miq_expression/tag_spec.rb index 860b998e2bc..2529c3a041a 100644 --- a/spec/lib/miq_expression/tag_spec.rb +++ b/spec/lib/miq_expression/tag_spec.rb @@ -47,9 +47,11 @@ expect(described_class.parse(tag)).to be_nil end - it "returns nil with invalid case managed-tag" do + it "supports managed-tag (no model)" do tag = "managed-service_level" - expect(described_class.parse(tag)).to be_nil + expect(described_class.parse(tag)).to have_attributes(:model => nil, + :associations => [], + :namespace => "/managed/service_level") end it "returns nil with invalid case managed" do diff --git a/spec/lib/miq_expression_spec.rb b/spec/lib/miq_expression_spec.rb index f46b37693fb..fb357e11b46 100644 --- a/spec/lib/miq_expression_spec.rb +++ b/spec/lib/miq_expression_spec.rb @@ -3152,4 +3152,85 @@ ) end end + + describe "#set_tagged_target" do + it "will substitute a new class into the expression" do + expression = described_class.new("CONTAINS" => {"tag" => "managed-environment", "value" => "prod"}) + + expression.set_tagged_target(Vm) + + expect(expression.exp).to eq("CONTAINS" => {"tag" => "Vm.managed-environment", "value" => "prod"}) + end + + it "will substitute a new class and associations into the expression" do + expression = described_class.new("CONTAINS" => {"tag" => "managed-environment", "value" => "prod"}) + + expression.set_tagged_target(Vm, ["host"]) + + expect(expression.exp).to eq("CONTAINS" => {"tag" => "Vm.host.managed-environment", "value" => "prod"}) + end + + it "can handle OR expressions" do + expression = described_class.new( + "OR" => [ + {"CONTAINS" => {"tag" => "managed-environment", "value" => "prod"}}, + {"CONTAINS" => {"tag" => "managed-location", "value" => "ny"}} + ] + ) + + expression.set_tagged_target(Vm) + + expected = { + "OR" => [ + {"CONTAINS" => {"tag" => "Vm.managed-environment", "value" => "prod"}}, + {"CONTAINS" => {"tag" => "Vm.managed-location", "value" => "ny"}} + ] + } + expect(expression.exp).to eq(expected) + end + + it "can handle AND expressions" do + expression = described_class.new( + "AND" => [ + {"CONTAINS" => {"tag" => "managed-environment", "value" => "prod"}}, + {"CONTAINS" => {"tag" => "managed-location", "value" => "ny"}} + ] + ) + + expression.set_tagged_target(Vm) + + expected = { + "AND" => [ + {"CONTAINS" => {"tag" => "Vm.managed-environment", "value" => "prod"}}, + {"CONTAINS" => {"tag" => "Vm.managed-location", "value" => "ny"}} + ] + } + expect(expression.exp).to eq(expected) + end + + it "can handle NOT expressions" do + expression = described_class.new("NOT" => {"CONTAINS" => {"tag" => "managed-environment", "value" => "prod"}}) + + expression.set_tagged_target(Vm) + + expected = {"NOT" => {"CONTAINS" => {"tag" => "Vm.managed-environment", "value" => "prod"}}} + expect(expression.exp).to eq(expected) + end + + it "will not change the target of fields" do + expression = described_class.new("=" => {"field" => "Vm-vendor", "value" => "redhat"}) + + expression.set_tagged_target(Host) + + expect(expression.exp).to eq("=" => {"field" => "Vm-vendor", "value" => "redhat"}) + end + + it "will not change the target of counts" do + expression = described_class.new("=" => {"count" => "Vm.disks", "value" => "1"}) + + expression.set_tagged_target(Host) + + expect(expression.exp).to eq("=" => {"count" => "Vm.disks", "value" => "1"}) + end + end end