Permalink
Browse files

Merge pull request #465 from swoop-inc/mb_add_fail_on_unknown_hash_ke…

…ys_option

Add validation of imported hash keys
  • Loading branch information...
2 parents 0044b6d + 466e99f commit d2bd6a2bd09ee58c1d9b38fdc6a422bcdf28eb80 @jkowens jkowens committed Nov 21, 2017
Showing with 94 additions and 0 deletions.
  1. +46 −0 lib/activerecord-import/import.rb
  2. +48 −0 test/import_test.rb
@@ -247,6 +247,9 @@ def support_setting_primary_key_of_imported_objects?
# BlogPost.import posts
#
# # Example using array_of_hash_objects
+ # # NOTE: column_names will be determined by using the keys of the first hash in the array. If later hashes in the
+ # # array have different keys an exception will be raised. If you have hashes to import with different sets of keys
+ # # we recommend grouping these into batches before importing.
# values = [ {author_name: 'zdennis', title: 'test post'} ], [ {author_name: 'jdoe', title: 'another test post'} ] ]
# BlogPost.import values
#
@@ -470,12 +473,18 @@ def import_helper( *args )
if args.length == 2
array_of_hashes = args.last
column_names = args.first.dup
+ allow_extra_hash_keys = true
else
array_of_hashes = args.first
column_names = array_of_hashes.first.keys
+ allow_extra_hash_keys = false
end
array_of_attributes = array_of_hashes.map do |h|
+ error_message = validate_hash_import(h, column_names, allow_extra_hash_keys)
+
+ raise ArgumentError, error_message if error_message
+
column_names.map do |key|
h[key]
end
@@ -850,5 +859,42 @@ def add_special_rails_stamps( column_names, array_of_attributes, options )
def validations_array_for_column_names_and_attributes( column_names, array_of_attributes ) # :nodoc:
array_of_attributes.map { |values| Hash[column_names.zip(values)] }
end
+
+ # Checks that the imported hash has the required_keys, optionally also checks that the hash has
+ # no keys beyond those required when `allow_extra_keys` is false.
+ # returns `nil` if validation passes, or an error message if it fails
+ def validate_hash_import(hash, required_keys, allow_extra_keys) # :nodoc:
+ extra_keys = allow_extra_keys ? [] : hash.keys - required_keys
+ missing_keys = required_keys - hash.keys
+
+ return nil if extra_keys.empty? && missing_keys.empty?
+
+ if allow_extra_keys
+ <<-EOS
+Hash key mis-match.
+
+When importing an array of hashes with provided columns_names, each hash must contain keys for all column_names.
+
+Required keys: #{column_names}
+Missing keys: #{missing_keys}
+
+Hash: #{hash}
+ EOS
+ else
+ <<-EOS
+Hash key mis-match.
+
+When importing an array of hashes, all hashes must have the same keys.
+If you have records that are missing some values, we recommend you either set default values
+for the missing keys or group these records into batches by key set before importing.
+
+Required keys: #{column_names}
+Extra keys: #{extra_keys}
+Missing keys: #{missing_keys}
+
+Hash: #{hash}
+ EOS
+ end
+ end
end
end
View
@@ -86,6 +86,54 @@
assert_nil t.author_email_address
end
end
+
+ context "with extra keys" do
+ let(:values) do
+ [
+ { title: "LDAP", author_name: "Jerry Carter" },
+ { title: "Rails Recipes", author_name: "Chad Fowler", author_email_address: "cfowler@test.com" } # author_email_address is unknown
+ ]
+ end
+
+ it "should fail when column names are not specified" do
+ err = assert_raises ArgumentError do
+ Topic.import values, validate: false
+ end
+
+ assert err.message.include? 'Extra keys: [:author_email_address]'
+ end
+
+ it "should succeed when column names are specified" do
+ assert_difference "Topic.count", +2 do
+ Topic.import columns, values, validate: false
+ end
+ end
+ end
+
+ context "with missing keys" do
+ let(:values) do
+ [
+ { title: "LDAP", author_name: "Jerry Carter" },
+ { title: "Rails Recipes" } # author_name is missing
+ ]
+ end
+
+ it "should fail when column names are not specified" do
+ err = assert_raises ArgumentError do
+ Topic.import values, validate: false
+ end
+
+ assert err.message.include? 'Missing keys: [:author_name]'
+ end
+
+ it "should fail on missing hash key from specified column names" do
+ err = assert_raises ArgumentError do
+ Topic.import %i(author_name), values, validate: false
+ end
+
+ assert err.message.include? 'Missing keys: [:author_name]'
+ end
+ end
end
unless ENV["SKIP_COMPOSITE_PK"]

0 comments on commit d2bd6a2

Please sign in to comment.