Skip to content

Commit

Permalink
Merge pull request ManageIQ#18645 from himdel/dialog-import
Browse files Browse the repository at this point in the history
Dialog import/export - add versioning, migrate load_values_on_init
  • Loading branch information
martinpovolny authored Jun 3, 2019
2 parents ff66d85 + 0804a10 commit f169faa
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 32 deletions.
14 changes: 13 additions & 1 deletion app/models/dialog_field_importer.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
class DialogFieldImporter
class InvalidDialogFieldTypeError < StandardError; end

def import_field(dialog_field_attributes)
def import_field(dialog_field_attributes, export_version = DialogImportService::CURRENT_DIALOG_VERSION)
if dialog_field_attributes["type"] == "DialogFieldDynamicList"
dialog_field_attributes["type"] = "DialogFieldDropDownList"
dialog_field_attributes["dynamic"] = true
end

if Gem::Version.new(export_version) < Gem::Version.new('5.11')
dialog_field_attributes["load_values_on_init"] = if !dialog_field_attributes["show_refresh_button"]
# no refresh button, always true
true
elsif dialog_field_attributes["load_values_on_init"].nil?
# unspecified, default to true
true
else
!!dialog_field_attributes["load_values_on_init"]
end
end

if DialogField::DIALOG_FIELD_TYPES.include?(dialog_field_attributes["type"])
dialog_field_type_class = dialog_field_attributes["type"].constantize
resource_action_attributes = dialog_field_attributes.delete("resource_action")
Expand Down
16 changes: 12 additions & 4 deletions app/models/dialog_import_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class InvalidDialogFieldTypeError < StandardError; end
class ParsedNonDialogYamlError < StandardError; end
class ParsedNonDialogError < StandardError; end
class DialogFieldAssociationCircularReferenceError < StandardError; end
class InvalidDialogVersionError < StandardError; end

def initialize(dialog_field_association_validator = DialogFieldAssociationValidator.new)
@dialog_field_association_validator = dialog_field_association_validator
Expand All @@ -17,8 +18,7 @@ def determine_validity(import_file_upload)
end

def determine_dialog_validity(dialog)
raise ParsedNonDialogError unless dialog['dialog_tabs']
check_dialog_tabs_for_validity(dialog['dialog_tabs'])
check_dialog_for_validity(dialog)
rescue ParsedNonDialogYamlError
raise ParsedNonDialogError, 'Not a valid dialog'
end
Expand All @@ -27,13 +27,21 @@ def determine_dialog_validity(dialog)

def check_dialogs_for_validity(dialogs)
dialogs.each do |dialog|
raise ParsedNonDialogYamlError unless dialog["dialog_tabs"]
check_dialog_tabs_for_validity(dialog["dialog_tabs"])
check_dialog_for_validity(dialog)
end
rescue TypeError
raise ParsedNonDialogYamlError
end

def check_dialog_for_validity(dialog)
raise ParsedNonDialogYamlError unless dialog['dialog_tabs']
raise InvalidDialogVersionError if dialog['export_version'].present? && dialog['export_version'] > DialogImportService::CURRENT_DIALOG_VERSION

check_dialog_tabs_for_validity(dialog['dialog_tabs'])
rescue TypeError
raise ParsedNonDialogYamlError
end

def check_dialog_tabs_for_validity(dialog_tabs)
dialog_tabs.each do |dialog_tab|
raise ParsedNonDialogYamlError unless dialog_tab["dialog_groups"]
Expand Down
2 changes: 1 addition & 1 deletion app/models/dialog_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def serialize_dialog_tabs(dialog_tabs, all_attributes)
def serialize_dialogs(dialogs, all_attributes)
dialogs.map do |dialog|
serialized_dialog_tabs = serialize_dialog_tabs(dialog.dialog_tabs, all_attributes)
included_attributes(dialog.attributes, all_attributes).merge("dialog_tabs" => serialized_dialog_tabs)
included_attributes(dialog.attributes, all_attributes).merge("dialog_tabs" => serialized_dialog_tabs, 'export_version' => DialogImportService::CURRENT_DIALOG_VERSION)
end
end
end
25 changes: 14 additions & 11 deletions lib/services/dialog_import_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ class ImportNonYamlError < StandardError; end
class ParsedNonDialogYamlError < StandardError; end
class DialogFieldAssociationCircularReferenceError < StandardError; end

DEFAULT_DIALOG_VERSION = '5.10'.freeze # assumed for dialogs without version info
CURRENT_DIALOG_VERSION = '5.11'.freeze

def initialize(dialog_field_importer = DialogFieldImporter.new, dialog_import_validator = DialogImportValidator.new, dialog_field_association_validator = DialogFieldAssociationValidator.new)
@dialog_field_importer = dialog_field_importer
@dialog_import_validator = dialog_import_validator
Expand All @@ -25,7 +28,7 @@ def import_from_file(filename)
if dialog_with_label?(dialog["label"])
yield dialog if block_given?
else
Dialog.create(dialog.merge("dialog_tabs" => build_dialog_tabs(dialog)))
Dialog.create(dialog.except('export_version').merge("dialog_tabs" => build_dialog_tabs(dialog, dialog['export_version'] || DEFAULT_DIALOG_VERSION)))
end
end
rescue DialogFieldImporter::InvalidDialogFieldTypeError
Expand Down Expand Up @@ -65,23 +68,23 @@ def store_for_import(file_contents)
queue_deletion(import_file_upload.id)
end

def build_dialog_tabs(dialog)
def build_dialog_tabs(dialog, export_version = CURRENT_DIALOG_VERSION)
dialog["dialog_tabs"].collect do |dialog_tab|
DialogTab.create(dialog_tab.merge("dialog_groups" => build_dialog_groups(dialog_tab)))
DialogTab.create(dialog_tab.merge("dialog_groups" => build_dialog_groups(dialog_tab, export_version)))
end
end

def build_dialog_groups(dialog_tab)
def build_dialog_groups(dialog_tab, export_version = CURRENT_DIALOG_VERSION)
dialog_tab["dialog_groups"].collect do |dialog_group|
DialogGroup.create(dialog_group.merge("dialog_fields" => build_dialog_fields(dialog_group)))
DialogGroup.create(dialog_group.merge("dialog_fields" => build_dialog_fields(dialog_group, export_version)))
end
end

def build_dialog_fields(dialog_group)
def build_dialog_fields(dialog_group, export_version = CURRENT_DIALOG_VERSION)
check_field_associations(dialog_group["dialog_fields"])
dialog_group["dialog_fields"].collect do |dialog_field|
dialog_field["options"].try(:symbolize_keys!)
@dialog_field_importer.import_field(dialog_field)
@dialog_field_importer.import_field(dialog_field, export_version)
end
end

Expand All @@ -98,9 +101,9 @@ def check_field_associations(fields)

def import(dialog)
@dialog_import_validator.determine_dialog_validity(dialog)
new_dialog = Dialog.create(dialog.except('dialog_tabs'))
new_dialog = Dialog.create(dialog.except('dialog_tabs', 'export_version'))
association_list = build_association_list(dialog)
new_dialog.update!(dialog.merge('dialog_tabs' => build_dialog_tabs(dialog)))
new_dialog.update!(dialog.merge('dialog_tabs' => build_dialog_tabs(dialog, dialog['export_version'] || DEFAULT_DIALOG_VERSION)))
build_associations(new_dialog, association_list)
new_dialog
end
Expand Down Expand Up @@ -146,8 +149,8 @@ def import_from_dialogs(dialogs)
dialog['id'] = new_or_existing_dialog.id
new_associations = build_association_list(dialog)
new_or_existing_dialog.update_attributes(
dialog.merge(
"dialog_tabs" => build_dialog_tabs(dialog),
dialog.except('export_version').merge(
"dialog_tabs" => build_dialog_tabs(dialog, dialog['export_version'] || DEFAULT_DIALOG_VERSION),
"resource_actions" => build_resource_actions(dialog)
)
)
Expand Down
33 changes: 30 additions & 3 deletions spec/lib/services/dialog_import_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
{"label" => "Test2", "dialog_tabs" => dialog_tabs, "description" => "potato", "blueprint_id" => "456"}]
end

let(:dialogs_with_current_version) do
[{"label" => "Test", "dialog_tabs" => dialog_tabs, 'export_version' => DialogImportService::CURRENT_DIALOG_VERSION}]
end

let(:not_dialogs) { [{"this is not" => "a dialog"}] }

before do
Expand Down Expand Up @@ -111,7 +115,7 @@
end

it "imports the dialog fields" do
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0])
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0], DialogImportService::DEFAULT_DIALOG_VERSION)
dialog_import_service.import_from_file(filename)
end
end
Expand Down Expand Up @@ -143,6 +147,29 @@
end.to raise_error(DialogImportService::ParsedNonDialogYamlError)
end
end

context "when the loaded yaml has unspecified version" do
before do
allow(YAML).to receive(:load_file).with(filename).and_return(dialogs)
end

it "defaults to DEFAULT_DIALOG_VERSION" do
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0], DialogImportService::DEFAULT_DIALOG_VERSION)
dialog_import_service.import_from_file(filename)
end
end

context "when the loaded yaml has valid version" do
let(:version) { DialogImportService::CURRENT_DIALOG_VERSION }
before do
allow(YAML).to receive(:load_file).with(filename).and_return(dialogs_with_current_version)
end

it "the version gets used" do
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0], version)
dialog_import_service.import_from_file(filename)
end
end
end

describe "#import_all_service_dialogs_from_yaml_file" do
Expand Down Expand Up @@ -182,7 +209,7 @@
end

it "imports the dialog fields" do
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0])
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0], DialogImportService::DEFAULT_DIALOG_VERSION)
dialog_import_service.import_all_service_dialogs_from_yaml_file("filename")
end

Expand Down Expand Up @@ -293,7 +320,7 @@
end

it "imports the dialog fields" do
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0])
expect(dialog_field_importer).to receive(:import_field).with(dialog_fields[0], DialogImportService::DEFAULT_DIALOG_VERSION)
dialog_import_service.import_service_dialogs(import_file_upload, dialogs_to_import)
end
end
Expand Down
25 changes: 21 additions & 4 deletions spec/lib/task_helpers/exports/service_dialogs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,29 @@
FileUtils.remove_entry export_dir
end

def load_yaml(filename)
data = File.read(filename)
YAML.safe_load(data)
end

def without_version(data)
data.map do |dialog|
dialog.except('export_version')
end
end

it 'exports service dialogs as individual files in a given directory' do
TaskHelpers::Exports::ServiceDialogs.new.export(:directory => export_dir)
file_contents = File.read("#{export_dir}/the_first_label.yaml")
file_contents2 = File.read("#{export_dir}/TheSecondLabel.yaml")
expect(YAML.safe_load(file_contents)).to eq(expected_data1)
expect(YAML.safe_load(file_contents2)).to eq(expected_data2)
file_contents = load_yaml("#{export_dir}/the_first_label.yaml")
file_contents2 = load_yaml("#{export_dir}/TheSecondLabel.yaml")
expect(without_version(file_contents)).to eq(expected_data1)
expect(without_version(file_contents2)).to eq(expected_data2)
expect(Dir[File.join(export_dir, '**', '*')].count { |file| File.file?(file) }).to eq(2)
end

it 'exports with current export_version' do
TaskHelpers::Exports::ServiceDialogs.new.export(:directory => export_dir)
file_contents = load_yaml("#{export_dir}/the_first_label.yaml")
expect(file_contents.first).to include('export_version' => DialogImportService::CURRENT_DIALOG_VERSION)
end
end
35 changes: 35 additions & 0 deletions spec/models/dialog_field_importer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,5 +284,40 @@
end.to raise_error(DialogFieldImporter::InvalidDialogFieldTypeError)
end
end

context "version migrations" do
let(:type) { "DialogFieldDynamicList" }
let(:dialog_field_show_false) do
dialog_field.merge('show_refresh_button' => false,
'load_values_on_init' => false)
end
let(:dialog_field_load_false) do
dialog_field.merge('show_refresh_button' => true,
'load_values_on_init' => false)
end

context "from version 1" do
context "load_values_on_init" do
it "is set to true when no refresh button" do
dialog_field_importer.import_field(dialog_field_show_false, DialogImportService::DEFAULT_DIALOG_VERSION)
expect(DialogField.first.load_values_on_init).to eq(true)
end

it "is preserved when refresh button" do
dialog_field_importer.import_field(dialog_field_load_false, DialogImportService::DEFAULT_DIALOG_VERSION)
expect(DialogField.first.load_values_on_init).to eq(false)
end
end
end

context "from current version" do
context "load_values_on_init" do
it "is not touched" do
dialog_field_importer.import_field(dialog_field_show_false)
expect(DialogField.first.load_values_on_init).to eq(false)
end
end
end
end
end
end
14 changes: 14 additions & 0 deletions spec/models/dialog_import_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,19 @@
end.to raise_error(DialogImportValidator::ParsedNonDialogError)
end
end

context 'when the loaded yaml has invalid version' do
let(:dialog_with_invalid_version) do
left, dot, last = DialogImportService::CURRENT_DIALOG_VERSION.rpartition('.')
version = "#{left}#{dot}#{last.to_i + 1}" # one more than current version
{"label" => "Test", "dialog_tabs" => [], 'export_version' => version}
end

it "raises a InvalidDialogVersionError" do
expect do
dialog_import_validator.determine_dialog_validity(dialog_with_invalid_version)
end.to raise_error(DialogImportValidator::InvalidDialogVersionError)
end
end
end
end
9 changes: 5 additions & 4 deletions spec/models/dialog_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@

let(:expected_data) do
{
"description" => description,
"buttons" => buttons,
"label" => label,
"dialog_tabs" => %w(serialized_dialog1 serialized_dialog2)
"description" => description,
"buttons" => buttons,
"label" => label,
"dialog_tabs" => %w[serialized_dialog1 serialized_dialog2],
"export_version" => DialogImportService::CURRENT_DIALOG_VERSION,
}
end

Expand Down
9 changes: 5 additions & 4 deletions spec/models/dialog_yaml_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@

let(:expected_data) do
{
"description" => description,
"buttons" => buttons,
"label" => label,
"dialog_tabs" => ["serialized_dialog1", "serialized_dialog2"],
"description" => description,
"buttons" => buttons,
"label" => label,
"dialog_tabs" => %w[serialized_dialog1 serialized_dialog2],
"export_version" => DialogImportService::CURRENT_DIALOG_VERSION,
}
end

Expand Down

0 comments on commit f169faa

Please sign in to comment.