From 49d412f02c9617e7ea400220f42db3c3c93bcad1 Mon Sep 17 00:00:00 2001 From: Lucy Fu Date: Fri, 27 Mar 2020 17:28:15 -0400 Subject: [PATCH 1/2] Add domain_name and partial_namespace to MiqAeNamespace. https://github.com/ManageIQ/manageiq-automation_engine/issues/410 --- ...d_partial_namespace_to_miq_ae_namespace.rb | 31 +++++++++++++++ ...tial_namespace_to_miq_ae_namespace_spec.rb | 39 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb create mode 100644 spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb diff --git a/db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb b/db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb new file mode 100644 index 000000000..d1d66e8e3 --- /dev/null +++ b/db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb @@ -0,0 +1,31 @@ +require 'ancestry' + +class AddDomainAndPartialNamespaceToMiqAeNamespace < ActiveRecord::Migration[5.1] + class MiqAeNamespace < ActiveRecord::Base + has_ancestry + end + + def up + add_column :miq_ae_namespaces, :domain_name, :string + add_column :miq_ae_namespaces, :partial_namespace, :string + add_index :miq_ae_namespaces, :domain_name + add_index :miq_ae_namespaces, :partial_namespace + + say_with_time("Add domain_name and partial_namespace to MiqAeNamespaces") do + MiqAeNamespace.find_each do |ns| + if ns.root? + ns.update(:domain_name => ns.name) + else + ns.update(:domain_name => ns.root.name, :partial_namespace => ns.path.pluck(:name).drop(1).join("/")) + end + end + end + end + + def down + remove_index :miq_ae_namespaces, :column => :domain_name + remove_index :miq_ae_namespaces, :column => :partial_namespace + remove_column :miq_ae_namespaces, :domain_name + remove_column :miq_ae_namespaces, :partial_namespace + end +end diff --git a/spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb b/spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb new file mode 100644 index 000000000..4fbc9a84b --- /dev/null +++ b/spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb @@ -0,0 +1,39 @@ +require_migration + +describe AddDomainAndPartialNamespaceToMiqAeNamespace do + let(:ns_stub) { Class.new(ActiveRecord::Base) { self.table_name = "miq_ae_namespaces" } } + + migration_context :up do + # nodes: + # ns1 + # ns11 + # ns111 + # ns112 + # ns2 + # ns21 + it "updates tree" do + ns1 = ns_stub.create!(:name => 'ns1') + ns2 = ns_stub.create!(:name => 'ns2') + ns11 = ns_stub.create!(:name => 'ns11', :ancestry => ns1.id.to_s) + ns111 = ns_stub.create!(:name => 'ns111', :ancestry => "#{ns1.id}/#{ns11.id}") + ns112 = ns_stub.create!(:name => 'ns112', :ancestry => "#{ns1.id}/#{ns11.id}") + ns21 = ns_stub.create!(:name => 'ns21', :ancestry => ns2.id.to_s) + + migrate + + expect(ns1.reload.domain_name).to eq(ns1.name) + expect(ns2.reload.domain_name).to eq(ns2.name) + expect(ns1.partial_namespace).to be_nil + expect(ns2.partial_namespace).to be_nil + + expect(ns11.reload.domain_name).to eq(ns1.name) + expect(ns111.reload.domain_name).to eq(ns1.name) + expect(ns112.reload.domain_name).to eq(ns1.name) + expect(ns21.reload.domain_name).to eq(ns2.name) + expect(ns11.partial_namespace).to eq("#{ns11.name}") + expect(ns111.partial_namespace).to eq("#{ns11.name}/#{ns111.name}") + expect(ns112.partial_namespace).to eq("#{ns11.name}/#{ns112.name}") + expect(ns21.partial_namespace).to eq("#{ns21.name}") + end + end +end From e5309fc5a80a2b7ec1de66b09ca20cad3a9a4b35 Mon Sep 17 00:00:00 2001 From: Lucy Fu Date: Fri, 3 Apr 2020 09:04:40 -0400 Subject: [PATCH 2/2] Add domain_id and relative_path to MiqAeNamespace/MiqAeClass/MiqAeInstance/MiqAeMethod. https://github.com/ManageIQ/manageiq-automation_engine/issues/410 --- ...d_partial_namespace_to_miq_ae_namespace.rb | 31 ---- ...dd_domain_and_relative_path_to_automate.rb | 124 ++++++++++++++++ ...tial_namespace_to_miq_ae_namespace_spec.rb | 39 ----- ...main_and_relative_path_to_automate_spec.rb | 139 ++++++++++++++++++ 4 files changed, 263 insertions(+), 70 deletions(-) delete mode 100644 db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb create mode 100644 db/migrate/20200325170358_add_domain_and_relative_path_to_automate.rb delete mode 100644 spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb create mode 100644 spec/migrations/20200325170358_add_domain_and_relative_path_to_automate_spec.rb diff --git a/db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb b/db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb deleted file mode 100644 index d1d66e8e3..000000000 --- a/db/migrate/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'ancestry' - -class AddDomainAndPartialNamespaceToMiqAeNamespace < ActiveRecord::Migration[5.1] - class MiqAeNamespace < ActiveRecord::Base - has_ancestry - end - - def up - add_column :miq_ae_namespaces, :domain_name, :string - add_column :miq_ae_namespaces, :partial_namespace, :string - add_index :miq_ae_namespaces, :domain_name - add_index :miq_ae_namespaces, :partial_namespace - - say_with_time("Add domain_name and partial_namespace to MiqAeNamespaces") do - MiqAeNamespace.find_each do |ns| - if ns.root? - ns.update(:domain_name => ns.name) - else - ns.update(:domain_name => ns.root.name, :partial_namespace => ns.path.pluck(:name).drop(1).join("/")) - end - end - end - end - - def down - remove_index :miq_ae_namespaces, :column => :domain_name - remove_index :miq_ae_namespaces, :column => :partial_namespace - remove_column :miq_ae_namespaces, :domain_name - remove_column :miq_ae_namespaces, :partial_namespace - end -end diff --git a/db/migrate/20200325170358_add_domain_and_relative_path_to_automate.rb b/db/migrate/20200325170358_add_domain_and_relative_path_to_automate.rb new file mode 100644 index 000000000..ee1046a94 --- /dev/null +++ b/db/migrate/20200325170358_add_domain_and_relative_path_to_automate.rb @@ -0,0 +1,124 @@ +require 'ancestry' + +class AddDomainAndRelativePathToAutomate < ActiveRecord::Migration[5.1] + class MiqAeDomain < ActiveRecord::Base + self.table_name = 'miq_ae_namespaces' + end + + class MiqAeNamespace < ActiveRecord::Base + has_many :ae_classes, :class_name => "AddDomainAndRelativePathToAutomate::MiqAeClass", :foreign_key => :namespace_id + has_ancestry + end + + class MiqAeClass < ActiveRecord::Base + self.inheritance_column = :_type_disabled + belongs_to :ae_namespace, :class_name => "AddDomainAndRelativePathToAutomate::MiqAeNamespace", :foreign_key => :namespace_id + end + + class MiqAeInstance < ActiveRecord::Base + belongs_to :ae_class, :class_name => "AddDomainAndRelativePathToAutomate::MiqAeClass", :foreign_key => :class_id + end + + class MiqAeMethod < ActiveRecord::Base + belongs_to :ae_class, :class_name => "AddDomainAndRelativePathToAutomate::MiqAeClass", :foreign_key => :class_id + end + + def populate_children(nodes) + nodes.each do |parent, children| + children.keys.each do |child| + child.update!(:relative_path => "#{parent.relative_path}/#{child.name}", :domain_id => parent.domain_id) + end + populate_children(children) + end + end + + # for miq_ae_class, object_name is nil + # joins namespace's relative_path, '/', miq_ae_classes' name + # for '$' domain, namespace is absent, so don't output the '/' + # for miq_ae_instance/miq_ae_method, object_name has table name + # joins namespace's relative_path, '/', class's name, '/', object_name's name + # need to join from object_name to ae_class + # for '$' domain, namespace is absent, so don't output the first '/' + # + def relative_path_sql(object_name = nil) + optional_slash_sql = "CASE WHEN miq_ae_namespaces.relative_path IS NULL THEN '' ELSE '/' END" + columns = ["miq_ae_namespaces.relative_path", optional_slash_sql, "miq_ae_classes.name"] + columns += ["'/'", "#{object_name}.name"] if object_name + + sql = MiqAeNamespace.select("concat(#{columns.join(', ')})") + if object_name + sql.joins(:ae_classes).where("#{object_name}.class_id = miq_ae_classes.id").to_sql + else + sql.where("miq_ae_namespaces.id = miq_ae_classes.namespace_id").to_sql + end + end + + # usually it is structured as /domain/namespace/class/method, namespace has domain_id set + # for '$' domain, it is structured like /$/class/method, namespace is the domain, domain_id is absent, so uses id + def domain_id_sql(object_name = nil) + sql = MiqAeNamespace.select("COALESCE(miq_ae_namespaces.domain_id, miq_ae_namespaces.id)") + if object_name + sql.joins(:ae_classes).where("#{object_name}.class_id = miq_ae_classes.id").to_sql + else + sql.where("miq_ae_namespaces.id = miq_ae_classes.namespace_id").to_sql + end + end + + def up + add_column :miq_ae_namespaces, :domain_id, :bigint + add_column :miq_ae_namespaces, :relative_path, :string + add_index :miq_ae_namespaces, :relative_path + + add_column :miq_ae_classes, :domain_id, :bigint + add_column :miq_ae_classes, :relative_path, :string + add_index :miq_ae_classes, :relative_path + + add_column :miq_ae_instances, :domain_id, :bigint + add_column :miq_ae_instances, :relative_path, :string + add_index :miq_ae_instances, :relative_path + + add_column :miq_ae_methods, :domain_id, :bigint + add_column :miq_ae_methods, :relative_path, :string + add_index :miq_ae_methods, :relative_path + + say_with_time("Add domain_id and relative_path to MiqAeNamespace") do + MiqAeNamespace.where(:ancestry => nil).each do |domain| + children = domain.descendants.arrange + children.keys.each do |child| + child.update!(:domain_id => domain.id, :relative_path => child.name) + end + populate_children(children) + end + end + + say_with_time("Add domain_id and relative_path to MiqAeClass") do + MiqAeClass.update_all("domain_id = (#{domain_id_sql}), relative_path = (#{relative_path_sql})") + end + + say_with_time("Add domain_id and relative_path to MiqAeInstance") do + MiqAeInstance.update_all("domain_id = (#{domain_id_sql("miq_ae_instances")}), relative_path = (#{relative_path_sql("miq_ae_instances")})") + end + + say_with_time("Add domain_id and relative_path to MiqAeMethod") do + MiqAeMethod.update_all("domain_id = (#{domain_id_sql("miq_ae_methods")}), relative_path = (#{relative_path_sql("miq_ae_methods")})") + end + end + + def down + remove_index :miq_ae_namespaces, :column => :relative_path + remove_column :miq_ae_namespaces, :domain_id + remove_column :miq_ae_namespaces, :relative_path + + remove_index :miq_ae_classes, :column => :relative_path + remove_column :miq_ae_classes, :domain_id + remove_column :miq_ae_classes, :relative_path + + remove_index :miq_ae_instances, :column => :relative_path + remove_column :miq_ae_instances, :domain_id + remove_column :miq_ae_instances, :relative_path + + remove_index :miq_ae_methods, :column => :relative_path + remove_column :miq_ae_methods, :domain_id + remove_column :miq_ae_methods, :relative_path + end +end diff --git a/spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb b/spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb deleted file mode 100644 index 4fbc9a84b..000000000 --- a/spec/migrations/20200325170358_add_domain_and_partial_namespace_to_miq_ae_namespace_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require_migration - -describe AddDomainAndPartialNamespaceToMiqAeNamespace do - let(:ns_stub) { Class.new(ActiveRecord::Base) { self.table_name = "miq_ae_namespaces" } } - - migration_context :up do - # nodes: - # ns1 - # ns11 - # ns111 - # ns112 - # ns2 - # ns21 - it "updates tree" do - ns1 = ns_stub.create!(:name => 'ns1') - ns2 = ns_stub.create!(:name => 'ns2') - ns11 = ns_stub.create!(:name => 'ns11', :ancestry => ns1.id.to_s) - ns111 = ns_stub.create!(:name => 'ns111', :ancestry => "#{ns1.id}/#{ns11.id}") - ns112 = ns_stub.create!(:name => 'ns112', :ancestry => "#{ns1.id}/#{ns11.id}") - ns21 = ns_stub.create!(:name => 'ns21', :ancestry => ns2.id.to_s) - - migrate - - expect(ns1.reload.domain_name).to eq(ns1.name) - expect(ns2.reload.domain_name).to eq(ns2.name) - expect(ns1.partial_namespace).to be_nil - expect(ns2.partial_namespace).to be_nil - - expect(ns11.reload.domain_name).to eq(ns1.name) - expect(ns111.reload.domain_name).to eq(ns1.name) - expect(ns112.reload.domain_name).to eq(ns1.name) - expect(ns21.reload.domain_name).to eq(ns2.name) - expect(ns11.partial_namespace).to eq("#{ns11.name}") - expect(ns111.partial_namespace).to eq("#{ns11.name}/#{ns111.name}") - expect(ns112.partial_namespace).to eq("#{ns11.name}/#{ns112.name}") - expect(ns21.partial_namespace).to eq("#{ns21.name}") - end - end -end diff --git a/spec/migrations/20200325170358_add_domain_and_relative_path_to_automate_spec.rb b/spec/migrations/20200325170358_add_domain_and_relative_path_to_automate_spec.rb new file mode 100644 index 000000000..917e87be4 --- /dev/null +++ b/spec/migrations/20200325170358_add_domain_and_relative_path_to_automate_spec.rb @@ -0,0 +1,139 @@ +require_migration + +describe AddDomainAndRelativePathToAutomate do + let(:dom_stub) { migration_stub(:MiqAeDomain) } + let(:ns_stub) { migration_stub(:MiqAeNamespace) } + let(:class_stub) { migration_stub(:MiqAeClass) } + let(:method_stub) { migration_stub(:MiqAeMethod) } + let(:instance_stub) { migration_stub(:MiqAeInstance) } + + migration_context :up do + it "handles domain without namespace" do + dom = dom_stub.create!(:name => 'dom') + + migrate + + assert_domain(dom) + end + + # nodes: + # dom + # ns + # class1 + # instance + # method + it "handles domain with single namespace" do + dom = dom_stub.create!(:name => 'dom') + ns = ns_stub.create!(:name => 'ns', :ancestry => dom.id.to_s) + class1 = class_stub.create!(:namespace_id => ns.id) + instance = instance_stub.create!(:class_id => class1.id) + method = method_stub.create!(:class_id => class1.id) + + migrate + + assert_domain(dom) + assert_object(ns, dom.id, ns.name) + assert_object(class1, dom.id, "#{ns.name}/#{class1.name}") + assert_object(instance, dom.id, "#{ns.name}/#{class1.name}/#{instance.name}") + assert_object(method, dom.id, "#{ns.name}/#{class1.name}/#{method.name}") + end + + # nodes: + # dom + # ns1 + # ns11 + # class1 + # instance + it "handles domain with multiple namespaces" do + dom = dom_stub.create!(:name => 'dom') + ns1 = ns_stub.create!(:name => 'ns1', :ancestry => dom.id.to_s) + ns11 = ns_stub.create!(:name => 'ns11', :ancestry => "#{dom.id}/#{ns1.id}") + class1 = class_stub.create!(:namespace_id => ns11.id) + instance = instance_stub.create!(:class_id => class1.id) + + migrate + + assert_domain(dom) + assert_object(ns1, dom.id, ns1.name) + assert_object(ns11, dom.id, "#{ns1.name}/#{ns11.name}") + assert_object(class1, dom.id, "#{ns1.name}/#{ns11.name}/#{class1.name}") + assert_object(instance, dom.id, "#{ns1.name}/#{ns11.name}/#{class1.name}/#{instance.name}") + end + + # nodes: + # dom + # ns1 + # ns11 + # ns12 + it "handles namespace without class" do + dom = dom_stub.create!(:name => 'dom') + ns1 = ns_stub.create!(:name => 'ns1', :ancestry => dom.id.to_s) + ns11 = ns_stub.create!(:name => 'ns11', :ancestry => "#{dom.id}/#{ns1.id}") + ns12 = ns_stub.create!(:name => 'ns12', :ancestry => "#{dom.id}/#{ns1.id}") + + migrate + + assert_domain(dom) + assert_object(ns1, dom.id, ns1.name) + assert_object(ns11, dom.id, "#{ns1.name}/#{ns11.name}") + assert_object(ns12, dom.id, "#{ns1.name}/#{ns12.name}") + end + + # nodes: + # dom + # ns + # class1 + # instance11 + # method12 + # class2 + # instance21 + # method22 + it "handles namespace with multiple classes" do + dom = dom_stub.create!(:name => 'dom') + ns = ns_stub.create!(:name => 'ns', :ancestry => dom.id.to_s) + class1 = class_stub.create!(:namespace_id => ns.id) + instance11 = instance_stub.create!(:class_id => class1.id) + method12 = method_stub.create!(:class_id => class1.id) + class2 = class_stub.create!(:namespace_id => ns.id) + instance21 = instance_stub.create!(:class_id => class2.id) + method22 = method_stub.create!(:class_id => class2.id) + + migrate + + assert_domain(dom) + assert_object(ns, dom.id, ns.name) + assert_object(class1, dom.id, "#{ns.name}/#{class1.name}") + assert_object(instance11, dom.id, "#{ns.name}/#{class1.name}/#{instance11.name}") + assert_object(method12, dom.id, "#{ns.name}/#{class1.name}/#{method12.name}") + assert_object(class2, dom.id, "#{ns.name}/#{class2.name}") + assert_object(instance21, dom.id, "#{ns.name}/#{class2.name}/#{instance21.name}") + assert_object(method22, dom.id, "#{ns.name}/#{class2.name}/#{method22.name}") + end + + # nodes: - Simulate the structure for '$' domain + # dom + # class1 + # method1 + it "handles class without namespace" do + dom = dom_stub.create!(:name => 'dom') + class1 = class_stub.create!(:name => 'class1', :namespace_id => dom.id) + method1 = method_stub.create!(:name => 'method1', :class_id => class1.id) + + migrate + + assert_domain(dom) + assert_object(class1, dom.id, class1.name) + assert_object(method1, dom.id, "#{class1.name}/#{method1.name}") + end + end + + def assert_domain(domain) + expect(domain.reload.domain_id).to be_nil + expect(domain.relative_path).to be_nil + end + + def assert_object(object, domain_id, relative_path) + expect(object.reload.domain_id).to eq(domain_id) + expect(object.relative_path).to eq(relative_path) + end +end