diff --git a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee index a378f00dd..776d0fc99 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee @@ -98,6 +98,7 @@ class App.ControllerGenericIndex extends App.Controller events: 'click [data-type=edit]': 'edit' 'click [data-type=new]': 'new' + 'click [data-type=import]': 'import' 'click .js-description': 'description' constructor: -> @@ -217,6 +218,10 @@ class App.ControllerGenericIndex extends App.Controller large: @large ) + import: (e) -> + e.preventDefault() + @importCallback() + description: (e) => new App.ControllerGenericDescription( description: App[ @genericObject ].description @@ -1275,3 +1280,123 @@ class App.ObserverActionRow extends App.ObserverController e.preventDefault() item.callback(object) ) + +class App.Import extends App.ControllerModal + buttonClose: true + buttonCancel: true + buttonSubmit: 'Import' + head: 'Import' + large: true + templateDirectory: 'generic/object_import' + baseUrl: '/api/v1/text_modules' + + content: => + + # show start dialog + content = $(App.view("#{@templateDirectory}/index")( + head: 'Import' + import_example_url: "#{@baseUrl}/import_example" + )) + + # check if data is processing... + if @data + result = App.view("#{@templateDirectory}/result")( + @data + ) + content.find('.js-error').html(result) + if result + content.find('.js-error').removeClass('hide') + else + content.find('.js-error').addClass('hide') + content + + onSubmit: (e) => + params = new FormData($(e.currentTarget).closest('form').get(0)) + params.set('try', true) + if _.isEmpty(params.get('data')) + params.delete('data') + @ajax( + id: 'csv_import' + type: 'POST' + url: "#{@baseUrl}/import" + processData: false + contentType: false + cache: false + data: params + success: (data, status, xhr) => + if data.result is 'success' + new App.ImportTryResult( + container: @el.closest('.content') + result: data + params: params + templateDirectory: @templateDirectory + baseUrl: @baseUrl + ) + @close() + return + @data = data + @update() + ) + +class App.ImportTryResult extends App.ControllerModal + buttonClose: true + buttonCancel: true + buttonSubmit: 'Yes, start real import.' + head: 'Import' + large: true + templateDirectory: 'generic/object_import/' + baseUrl: '/api/v1/text_modules' + + content: => + + # show start dialog + content = $(App.view("#{@templateDirectory}/import_try")( + head: 'Import' + import_example_url: "#{@baseUrl}/import" + result: @result + )) + content + + onSubmit: (e) => + @params.set('try', false) + @ajax( + id: 'csv_import' + type: 'POST' + url: "#{@baseUrl}/import" + processData: false + contentType: false + cache: false + data: @params + success: (data, status, xhr) => + if data.result is 'success' + new App.ImportResult( + container: @el.closest('.content') + result: data + params: @params + templateDirectory: @templateDirectory + baseUrl: @baseUrl + ) + @close() + return + @data = data + @update() + ) + +class App.ImportResult extends App.ControllerModal + buttonClose: true + buttonCancel: true + buttonSubmit: 'Close' + head: 'Import' + large: true + templateDirectory: 'generic/object_import/' + + content: => + + content = $(App.view("#{@templateDirectory}/imported")( + head: 'Imported' + result: @result + )) + content + + onSubmit: (e) => + @close() \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/organizations.coffee b/app/assets/javascripts/app/controllers/organizations.coffee index 0e0f0b1da..3135c83fc 100644 --- a/app/assets/javascripts/app/controllers/organizations.coffee +++ b/app/assets/javascripts/app/controllers/organizations.coffee @@ -8,6 +8,11 @@ class Index extends App.ControllerSubContent el: @el id: @id genericObject: 'Organization' + importCallback: -> + new App.Import( + baseUrl: '/api/v1/organizations' + container: @el.closest('.content') + ) pageData: home: 'organizations' object: 'Organization' @@ -17,6 +22,7 @@ class Index extends App.ControllerSubContent 'Organizations are for any person in the system. Agents (Owners, Resposbiles, ...) and Customers.' ] buttons: [ + { name: 'Import', 'data-type': 'import', class: 'btn' } { name: 'New Organization', 'data-type': 'new', class: 'btn--success' } ] container: @el.closest('.content') diff --git a/app/assets/javascripts/app/controllers/text_module.coffee b/app/assets/javascripts/app/controllers/text_module.coffee index 851a55eaf..defadbbef 100644 --- a/app/assets/javascripts/app/controllers/text_module.coffee +++ b/app/assets/javascripts/app/controllers/text_module.coffee @@ -8,6 +8,11 @@ class Index extends App.ControllerSubContent el: @el id: @id genericObject: 'TextModule' + importCallback: -> + new App.Import( + baseUrl: '/api/v1/text_modules' + container: @el.closest('.content') + ) pageData: home: 'text_modules' object: 'TextModule' @@ -17,6 +22,7 @@ class Index extends App.ControllerSubContent 'Text modules are ...' ] buttons: [ + { name: 'Import', 'data-type': 'import', class: 'btn' } { name: 'New text module', 'data-type': 'new', class: 'btn--success' } ] container: @el.closest('.content') diff --git a/app/assets/javascripts/app/controllers/users.coffee b/app/assets/javascripts/app/controllers/users.coffee index 16d4a7132..32a4d4db9 100644 --- a/app/assets/javascripts/app/controllers/users.coffee +++ b/app/assets/javascripts/app/controllers/users.coffee @@ -5,6 +5,7 @@ class Index extends App.ControllerSubContent '.js-search': 'searchInput' events: 'click [data-type=new]': 'new' + 'click [data-type=import]': 'import' constructor: -> super @@ -14,6 +15,7 @@ class Index extends App.ControllerSubContent @html App.view('user')( head: 'Users' buttons: [ + { name: 'Import', 'data-type': 'import', class: 'btn' } { name: 'New User', 'data-type': 'new', class: 'btn--success' } ] roles: App.Role.all() @@ -192,4 +194,11 @@ class Index extends App.ControllerSubContent callback: @recent ) + import: (e) -> + e.preventDefault() + new App.Import( + baseUrl: '/api/v1/users' + container: @el.closest('.content') + ) + App.Config.set( 'User', { prio: 1000, name: 'Users', parent: '#manage', target: '#manage/users', controller: Index, permission: ['admin.user'] }, 'NavBarAdmin' ) diff --git a/app/assets/javascripts/app/lib/bootstrap/modal.js b/app/assets/javascripts/app/lib/bootstrap/modal.js index bacf76633..8224c7051 100644 --- a/app/assets/javascripts/app/lib/bootstrap/modal.js +++ b/app/assets/javascripts/app/lib/bootstrap/modal.js @@ -185,7 +185,6 @@ // me - 2018-05-24 // cleanup element on hide - cleanup dom with old modal dialogs Modal.prototype.remove = function () { - console.log('remove', this.$element) this.$element.remove() } diff --git a/app/assets/javascripts/app/views/generic/object_import/import_try.jst.eco b/app/assets/javascripts/app/views/generic/object_import/import_try.jst.eco new file mode 100644 index 000000000..5d80e6c03 --- /dev/null +++ b/app/assets/javascripts/app/views/generic/object_import/import_try.jst.eco @@ -0,0 +1,21 @@ +
+

+ +

+<% if @result.stats: %> + <%- @T('The test run was successful.') %> + <%- @T('The following changes are made:') %> +

\ No newline at end of file diff --git a/app/assets/javascripts/app/views/generic/object_import/imported.jst.eco b/app/assets/javascripts/app/views/generic/object_import/imported.jst.eco new file mode 100644 index 000000000..2e307a79f --- /dev/null +++ b/app/assets/javascripts/app/views/generic/object_import/imported.jst.eco @@ -0,0 +1,28 @@ +
+<% if @errors: %> +
+ +
+

+

+<% if @result.stats: %> + <%- @T('The import was successful.') %> + <%- @T('The following changes have been made:') %> +

\ No newline at end of file diff --git a/app/assets/javascripts/app/views/generic/object_import/index.jst.eco b/app/assets/javascripts/app/views/generic/object_import/index.jst.eco new file mode 100644 index 000000000..c4725c189 --- /dev/null +++ b/app/assets/javascripts/app/views/generic/object_import/index.jst.eco @@ -0,0 +1,29 @@ +
+

+ +

+ <%- @T('Bulk import allows you to create and update many records at once.') %> + <%- @T('The data must be in the comma separated values (CSV) format and saved as UTF-8. You can import a CSV file or paste the data directly into the text area.') %> +

+ +

<%- @T('Alternatively, you can use the Zammad API to import data.') %>

+ +

<%- @T('Create new records') %>

+<%- @T('Records that exist in the import data (but not in Zammad) will always be created.') %> + +

<%- @T('Update existing records') %>

+<%- @T('Update existing records with the attributes specified in the import data.') %> + + + +

<%- @T('Select CSV file') %>

+ + +

<%- @T('Paste in CSV data') %>

+ + +

<%- @T('Note') %>: <%- @T('Example CSV file for download.') %>

+
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/generic/object_import/result.jst.eco b/app/assets/javascripts/app/views/generic/object_import/result.jst.eco new file mode 100644 index 000000000..d1676227c --- /dev/null +++ b/app/assets/javascripts/app/views/generic/object_import/result.jst.eco @@ -0,0 +1,9 @@ +
+<%- @T('Result') %>: <%= @result %> +<% if @errors: %> +
\ No newline at end of file diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index c9b1cc72f..bf5715181 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -347,10 +347,11 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co # @response_message 401 Invalid session. def import_start permission_check('admin.user') + string = params[:data] || params[:file].read.force_encoding('utf-8') result = Organization.csv_import( - string: params[:file].read.force_encoding('utf-8'), + string: string, parse_params: { - col_sep: ';', + col_sep: params[:col_sep] || ',', }, try: params[:try], ) diff --git a/app/controllers/text_modules_controller.rb b/app/controllers/text_modules_controller.rb index cd3f414e0..970663776 100644 --- a/app/controllers/text_modules_controller.rb +++ b/app/controllers/text_modules_controller.rb @@ -164,11 +164,11 @@ curl http://localhost/api/v1/text_modules.json -v -u #{login}:#{password} -H "Co def import_example permission_check('admin.text_module') csv_string = TextModule.csv_example( - col_sep: ',', + col_sep: params[:col_sep] || ',', ) send_data( csv_string, - filename: 'example.csv', + filename: 'text_module-example.csv', type: 'text/csv', disposition: 'attachment' ) @@ -186,10 +186,11 @@ curl http://localhost/api/v1/text_modules.json -v -u #{login}:#{password} -H "Co # @response_message 401 Invalid session. def import_start permission_check('admin.text_module') + string = params[:data] || params[:file].read.force_encoding('utf-8') result = TextModule.csv_import( - string: params[:file].read.force_encoding('utf-8'), + string: string, parse_params: { - col_sep: ';', + col_sep: params[:col_sep] || ',', }, try: params[:try], ) diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb index 43403b2de..a38a5ce23 100644 --- a/app/controllers/tickets_controller.rb +++ b/app/controllers/tickets_controller.rb @@ -629,10 +629,11 @@ class TicketsController < ApplicationController if Setting.get('import_mode') != true raise 'Only can import tickets if system is in import mode.' end + string = params[:data] || params[:file].read.force_encoding('utf-8') result = Ticket.csv_import( - string: params[:file].read.force_encoding('utf-8'), + string: string, parse_params: { - col_sep: ';', + col_sep: params[:col_sep] || ',', }, try: params[:try], ) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 9cb3a9020..1ec7a3615 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1100,10 +1100,11 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content # @response_message 401 Invalid session. def import_start permission_check('admin.user') + string = params[:data] || params[:file].read.force_encoding('utf-8') result = User.csv_import( - string: params[:file].read.force_encoding('utf-8'), + string: string, parse_params: { - col_sep: ';', + col_sep: params[:col_sep] || ',', }, try: params[:try], ) diff --git a/app/models/concerns/can_csv_import.rb b/app/models/concerns/can_csv_import.rb index 81c3cfe58..0338e8137 100644 --- a/app/models/concerns/can_csv_import.rb +++ b/app/models/concerns/can_csv_import.rb @@ -45,6 +45,7 @@ returns =end def csv_import(data) + errors = [] if data[:file].present? raise Exceptions::UnprocessableEntity, "No such file '#{data[:file]}'" if !File.exist?(data[:file]) @@ -56,13 +57,25 @@ returns end end if data[:string].blank? - raise Exceptions::UnprocessableEntity, 'Unable to parse empty file/string!' + errors.push "Unable to parse empty file/string for #{new.class}." + result = { + errors: errors, + try: data[:try], + result: 'failed', + } + return result end rows = ::CSV.parse(data[:string], data[:parse_params]) header = rows.shift if header.blank? - raise Exceptions::UnprocessableEntity, 'Unable to parse file/string without header!' + errors.push "Unable to parse file/string without header for #{new.class}." + result = { + errors: errors, + try: data[:try], + result: 'failed', + } + return result end header.each do |item| if item.respond_to?(:strip!) @@ -72,6 +85,16 @@ returns item.downcase! end + if rows[0].blank? + errors.push "No records found in file/string for #{new.class}." + result = { + errors: errors, + try: data[:try], + result: 'failed', + } + return result + end + # get payload based on csv payload = [] rows.each do |row| @@ -79,6 +102,7 @@ returns payload_last = payload.last row.each_with_index do |item, count| next if item.blank? + next if header[count].nil? if payload_last[header[count].to_sym].class != Array payload_last[header[count].to_sym] = [payload_last[header[count].to_sym]] end @@ -110,7 +134,6 @@ returns created: 0, updated: 0, } - errors = [] line_count = 0 payload.each do |attributes| line_count += 1 diff --git a/test/controllers/organization_controller_test.rb b/test/controllers/organization_controller_test.rb index 2aeebeb46..56d10490e 100644 --- a/test/controllers/organization_controller_test.rb +++ b/test/controllers/organization_controller_test.rb @@ -532,7 +532,7 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest # invalid file csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'organization_simple_col_not_existing.csv'), 'text/csv') - post '/api/v1/organizations/import?try=true', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/organizations/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) @@ -546,7 +546,7 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest # valid file try csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'organization_simple.csv'), 'text/csv') - post '/api/v1/organizations/import?try=true', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/organizations/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) @@ -560,7 +560,7 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest # valid file csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'organization_simple.csv'), 'text/csv') - post '/api/v1/organizations/import', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/organizations/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) diff --git a/test/controllers/text_module_controller_test.rb b/test/controllers/text_module_controller_test.rb index 077a18261..c028df4fa 100644 --- a/test/controllers/text_module_controller_test.rb +++ b/test/controllers/text_module_controller_test.rb @@ -102,7 +102,7 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest # invalid file csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'text_module_simple_col_not_existing.csv'), 'text/csv') - post '/api/v1/text_modules/import?try=true', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/text_modules/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) @@ -116,7 +116,7 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest # valid file try csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'text_module_simple.csv'), 'text/csv') - post '/api/v1/text_modules/import?try=true', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/text_modules/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) @@ -130,7 +130,7 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest # valid file csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'text_module_simple.csv'), 'text/csv') - post '/api/v1/text_modules/import', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/text_modules/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) diff --git a/test/controllers/user_controller_test.rb b/test/controllers/user_controller_test.rb index a5d5ed638..eaed69625 100644 --- a/test/controllers/user_controller_test.rb +++ b/test/controllers/user_controller_test.rb @@ -979,7 +979,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest # invalid file csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'user_simple_col_not_existing.csv'), 'text/csv') - post '/api/v1/users/import?try=true', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/users/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) @@ -993,7 +993,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest # valid file try csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'user_simple.csv'), 'text/csv') - post '/api/v1/users/import?try=true', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/users/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) @@ -1007,7 +1007,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest # valid file csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'user_simple.csv'), 'text/csv') - post '/api/v1/users/import', params: { file: csv_file }, headers: { 'Authorization' => credentials } + post '/api/v1/users/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials } assert_response(200) result = JSON.parse(@response.body) assert_equal(Hash, result.class) diff --git a/test/unit/organization_csv_import_test.rb b/test/unit/organization_csv_import_test.rb index 9979dd435..8246a89a1 100644 --- a/test/unit/organization_csv_import_test.rb +++ b/test/unit/organization_csv_import_test.rb @@ -18,6 +18,34 @@ class OrganizationCsvImportTest < ActiveSupport::TestCase assert(header.include?('members')) end + test 'empty payload' do + csv_string = '' + result = Organization.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert_nil(result[:records]) + assert_equal('failed', result[:result]) + assert_equal('Unable to parse empty file/string for Organization.', result[:errors][0]) + + csv_string = 'id;name;shared;domain;domain_assignment;active;' + result = Organization.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert(result[:records].blank?) + assert_equal('failed', result[:result]) + assert_equal('No records found in file/string for Organization.', result[:errors][0]) + end + test 'simple import' do csv_string = "id;name;shared;domain;domain_assignment;active;note\n;org-simple-import1;true;org-simple-import1.example.com;false;true;some note1\n;org-simple-import2;true;org-simple-import2.example.com;false;false;some note2\n" diff --git a/test/unit/text_module_csv_import_test.rb b/test/unit/text_module_csv_import_test.rb index af0208284..a06d000a2 100644 --- a/test/unit/text_module_csv_import_test.rb +++ b/test/unit/text_module_csv_import_test.rb @@ -22,6 +22,34 @@ class TextModuleCsvImportTest < ActiveSupport::TestCase assert_not(header.include?('customer')) end + test 'empty payload' do + csv_string = '' + result = TextModule.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert_nil(result[:records]) + assert_equal('failed', result[:result]) + assert_equal('Unable to parse empty file/string for TextModule.', result[:errors][0]) + + csv_string = 'name;keywords;content;note;active;' + result = TextModule.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert(result[:records].blank?) + assert_equal('failed', result[:result]) + assert_equal('No records found in file/string for TextModule.', result[:errors][0]) + end + test 'simple import' do csv_string = "name;keywords;content;note;active;\nsome name1;keyword1;\"some\ncontent1\";-;\nsome name2;keyword2;some content
test123\n" diff --git a/test/unit/ticket_csv_import_test.rb b/test/unit/ticket_csv_import_test.rb index fdd7ce8d8..ef6a5de51 100644 --- a/test/unit/ticket_csv_import_test.rb +++ b/test/unit/ticket_csv_import_test.rb @@ -22,6 +22,34 @@ class TicketCsvImportTest < ActiveSupport::TestCase end + test 'empty payload' do + csv_string = '' + result = Ticket.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert_nil(result[:records]) + assert_equal('failed', result[:result]) + assert_equal('Unable to parse empty file/string for Ticket.', result[:errors][0]) + + csv_string = 'id;number;title;state;priority;' + result = Ticket.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert(result[:records].blank?) + assert_equal('failed', result[:result]) + assert_equal('No records found in file/string for Ticket.', result[:errors][0]) + end + test 'simple import' do csv_string = "id;number;title;state;priority;owner;customer;group;note\n;123456;some title1;new;2 normal;-;nicole.braun@zammad.org;Users;some note1\n;123457;some title2;closed;1 low;admin@example.com;nicole.braun@zammad.org;Users;some note2\n" diff --git a/test/unit/user_csv_import_test.rb b/test/unit/user_csv_import_test.rb index c6d111b8d..8e83863d7 100644 --- a/test/unit/user_csv_import_test.rb +++ b/test/unit/user_csv_import_test.rb @@ -17,6 +17,34 @@ class UserCsvImportTest < ActiveSupport::TestCase assert(header.include?('organization')) end + test 'empty payload' do + csv_string = '' + result = User.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert_nil(result[:records]) + assert_equal('failed', result[:result]) + assert_equal('Unable to parse empty file/string for User.', result[:errors][0]) + + csv_string = "login;firstname;lastname;email;active;\n" + result = User.csv_import( + string: csv_string, + parse_params: { + col_sep: ';', + }, + try: true, + ) + assert_equal(true, result[:try]) + assert(result[:records].blank?) + assert_equal('failed', result[:result]) + assert_equal('No records found in file/string for User.', result[:errors][0]) + end + test 'simple import' do csv_string = "login;firstname;lastname;email;active;\nuser-simple-import1;firstname-simple-import1;lastname-simple-import1;user-simple-import1@example.com;true\nuser-simple-import2;firstname-simple-import2;lastname-simple-import2;user-simple-import2@example.com;false\n"