diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb
index 1372d82c4..823109586 100644
--- a/app/controllers/organizations_controller.rb
+++ b/app/controllers/organizations_controller.rb
@@ -323,4 +323,43 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co
render json: history
end
+ # @path [GET] /organizations/import_example
+ #
+ # @summary Download of example CSV file.
+ # @notes The requester have 'admin.organization' permissions to be able to download it.
+ # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/organizations/import_example
+ #
+ # @response_message 200 File download.
+ # @response_message 401 Invalid session.
+ def import_example
+ permission_check('admin.organization')
+ send_data(
+ Organization.csv_example,
+ filename: 'organization-example.csv',
+ type: 'text/csv',
+ disposition: 'attachment'
+ )
+ end
+
+ # @path [POST] /organizations/import
+ #
+ # @summary Starts import.
+ # @notes The requester have 'admin.text_module' permissions to be create a new import.
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/organizations.csv' 'https://your.zammad/api/v1/organizations/import?try=true'
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/organizations.csv' 'https://your.zammad/api/v1/organizations/import'
+ #
+ # @response_message 201 Import started.
+ # @response_message 401 Invalid session.
+ def import_start
+ permission_check('admin.user')
+ result = Organization.csv_import(
+ string: params[:file].read.force_encoding('utf-8'),
+ parse_params: {
+ col_sep: ';',
+ },
+ try: params[:try],
+ )
+ render json: result, status: :ok
+ end
+
end
diff --git a/app/controllers/text_modules_controller.rb b/app/controllers/text_modules_controller.rb
index dada3340c..cd3f414e0 100644
--- a/app/controllers/text_modules_controller.rb
+++ b/app/controllers/text_modules_controller.rb
@@ -152,4 +152,48 @@ curl http://localhost/api/v1/text_modules.json -v -u #{login}:#{password} -H "Co
permission_check('admin.text_module')
model_destroy_render(TextModule, params)
end
+
+ # @path [GET] /text_modules/import_example
+ #
+ # @summary Download of example CSV file.
+ # @notes The requester have 'admin.text_module' permissions to be able to download it.
+ # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/text_modules/import_example
+ #
+ # @response_message 200 File download.
+ # @response_message 401 Invalid session.
+ def import_example
+ permission_check('admin.text_module')
+ csv_string = TextModule.csv_example(
+ col_sep: ',',
+ )
+ send_data(
+ csv_string,
+ filename: 'example.csv',
+ type: 'text/csv',
+ disposition: 'attachment'
+ )
+
+ end
+
+ # @path [POST] /text_modules/import
+ #
+ # @summary Starts import.
+ # @notes The requester have 'admin.text_module' permissions to be create a new import.
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/Textbausteine_final2.csv' 'https://your.zammad/api/v1/text_modules/import?try=true'
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/Textbausteine_final2.csv' 'https://your.zammad/api/v1/text_modules/import'
+ #
+ # @response_message 201 Import started.
+ # @response_message 401 Invalid session.
+ def import_start
+ permission_check('admin.text_module')
+ result = TextModule.csv_import(
+ string: params[:file].read.force_encoding('utf-8'),
+ parse_params: {
+ col_sep: ';',
+ },
+ try: params[:try],
+ )
+ render json: result, status: :ok
+ end
+
end
diff --git a/app/controllers/ticket_articles_controller.rb b/app/controllers/ticket_articles_controller.rb
index 35f845a0e..2e1084023 100644
--- a/app/controllers/ticket_articles_controller.rb
+++ b/app/controllers/ticket_articles_controller.rb
@@ -276,6 +276,52 @@ class TicketArticlesController < ApplicationController
)
end
+ # @path [GET] /ticket_articles/import_example
+ #
+ # @summary Download of example CSV file.
+ # @notes The requester have 'admin' permissions to be able to download it.
+ # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/ticket_articles/import_example
+ #
+ # @response_message 200 File download.
+ # @response_message 401 Invalid session.
+ def import_example
+ permission_check('admin')
+ csv_string = Ticket::Article.csv_example(
+ col_sep: ',',
+ )
+ send_data(
+ csv_string,
+ filename: 'example.csv',
+ type: 'text/csv',
+ disposition: 'attachment'
+ )
+
+ end
+
+ # @path [POST] /ticket_articles/import
+ #
+ # @summary Starts import.
+ # @notes The requester have 'admin' permissions to be create a new import.
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/ticket_articles.csv' 'https://your.zammad/api/v1/ticket_articles/import?try=true'
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/ticket_articles.csv' 'https://your.zammad/api/v1/ticket_articles/import'
+ #
+ # @response_message 201 Import started.
+ # @response_message 401 Invalid session.
+ def import_start
+ permission_check('admin')
+ if Setting.get('import_mode') != true
+ raise 'Only can import tickets if system is in import mode.'
+ end
+ result = Ticket::Article.csv_import(
+ string: params[:file].read.force_encoding('utf-8'),
+ parse_params: {
+ col_sep: ';',
+ },
+ try: params[:try],
+ )
+ render json: result, status: :ok
+ end
+
private
def sanitized_disposition
diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb
index dbdf36ef6..6c326b5f0 100644
--- a/app/controllers/tickets_controller.rb
+++ b/app/controllers/tickets_controller.rb
@@ -598,6 +598,52 @@ class TicketsController < ApplicationController
}
end
+ # @path [GET] /tickets/import_example
+ #
+ # @summary Download of example CSV file.
+ # @notes The requester have 'admin' permissions to be able to download it.
+ # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/tickets/import_example
+ #
+ # @response_message 200 File download.
+ # @response_message 401 Invalid session.
+ def import_example
+ permission_check('admin')
+ csv_string = Ticket.csv_example(
+ col_sep: ',',
+ )
+ send_data(
+ csv_string,
+ filename: 'example.csv',
+ type: 'text/csv',
+ disposition: 'attachment'
+ )
+
+ end
+
+ # @path [POST] /tickets/import
+ #
+ # @summary Starts import.
+ # @notes The requester have 'admin' permissions to be create a new import.
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/tickets.csv' 'https://your.zammad/api/v1/tickets/import?try=true'
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/tickets.csv' 'https://your.zammad/api/v1/tickets/import'
+ #
+ # @response_message 201 Import started.
+ # @response_message 401 Invalid session.
+ def import_start
+ permission_check('admin')
+ if Setting.get('import_mode') != true
+ raise 'Only can import tickets if system is in import mode.'
+ end
+ result = Ticket.csv_import(
+ string: params[:file].read.force_encoding('utf-8'),
+ parse_params: {
+ col_sep: ';',
+ },
+ try: params[:try],
+ )
+ render json: result, status: :ok
+ end
+
private
def follow_up_possible_check
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index dc20b18ef..f3c982d3e 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1061,6 +1061,45 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content
render json: { avatars: result }, status: :ok
end
+ # @path [GET] /users/import_example
+ #
+ # @summary Download of example CSV file.
+ # @notes The requester have 'admin.user' permissions to be able to download it.
+ # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/users/import_example
+ #
+ # @response_message 200 File download.
+ # @response_message 401 Invalid session.
+ def import_example
+ permission_check('admin.user')
+ send_data(
+ User.csv_example,
+ filename: 'user-example.csv',
+ type: 'text/csv',
+ disposition: 'attachment'
+ )
+ end
+
+ # @path [POST] /users/import
+ #
+ # @summary Starts import.
+ # @notes The requester have 'admin.text_module' permissions to be create a new import.
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/users.csv' 'https://your.zammad/api/v1/users/import?try=true'
+ # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/users.csv' 'https://your.zammad/api/v1/users/import'
+ #
+ # @response_message 201 Import started.
+ # @response_message 401 Invalid session.
+ def import_start
+ permission_check('admin.user')
+ result = User.csv_import(
+ string: params[:file].read.force_encoding('utf-8'),
+ parse_params: {
+ col_sep: ';',
+ },
+ try: params[:try],
+ )
+ render json: result, status: :ok
+ end
+
private
def password_policy(password)
diff --git a/app/models/application_model/can_associations.rb b/app/models/application_model/can_associations.rb
index 2a625f19e..979054c5a 100644
--- a/app/models/application_model/can_associations.rb
+++ b/app/models/application_model/can_associations.rb
@@ -63,6 +63,9 @@ returns
real_values = real_values.to_sym
next if !respond_to?(real_values)
next if !params[real_values]
+ if params[real_values].instance_of?(String) || params[real_values].instance_of?(Integer) || params[real_values].instance_of?(Float)
+ params[real_values] = [params[real_values]]
+ end
next if !params[real_values].instance_of?(Array)
list = []
class_object = assoc.klass
diff --git a/app/models/application_model/can_lookup.rb b/app/models/application_model/can_lookup.rb
index b5f8e1ae4..61bd21898 100644
--- a/app/models/application_model/can_lookup.rb
+++ b/app/models/application_model/can_lookup.rb
@@ -39,10 +39,9 @@ returns
where(name: data[:name])
end
records.each do |loop_record|
- if loop_record.name == data[:name]
- cache_set(data[:name], loop_record)
- return loop_record
- end
+ next if loop_record.name != data[:name]
+ cache_set(data[:name], loop_record)
+ return loop_record
end
return
elsif data[:login]
@@ -56,10 +55,9 @@ returns
where(login: data[:login])
end
records.each do |loop_record|
- if loop_record.login == data[:login]
- cache_set(data[:login], loop_record)
- return loop_record
- end
+ next if loop_record.login != data[:login]
+ cache_set(data[:login], loop_record)
+ return loop_record
end
return
elsif data[:email]
@@ -73,15 +71,27 @@ returns
where(email: data[:email])
end
records.each do |loop_record|
- if loop_record.email == data[:email]
- cache_set(data[:email], loop_record)
- return loop_record
- end
+ next if loop_record.email != data[:email]
+ cache_set(data[:email], loop_record)
+ return loop_record
+ end
+ return
+ elsif data[:number]
+
+ # do lookup with == to handle case insensitive databases
+ records = if Rails.application.config.db_case_sensitive
+ where('LOWER(number) = LOWER(?)', data[:number])
+ else
+ where(number: data[:number])
+ end
+ records.each do |loop_record|
+ next if loop_record.number != data[:number]
+ return loop_record
end
return
end
- raise ArgumentError, 'Need name, id, login or email for lookup()'
+ raise ArgumentError, 'Need name, id, number, login or email for lookup()'
end
end
end
diff --git a/app/models/concerns/can_csv_import.rb b/app/models/concerns/can_csv_import.rb
new file mode 100644
index 000000000..ea8e49c80
--- /dev/null
+++ b/app/models/concerns/can_csv_import.rb
@@ -0,0 +1,351 @@
+# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
+require 'csv'
+
+module CanCsvImport
+ extend ActiveSupport::Concern
+
+ # methods defined here are going to extend the class, not the instance of it
+ class_methods do
+
+=begin
+
+ result = Model.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ',',
+ },
+ try: true,
+ )
+
+ result = Model.csv_import(
+ file: '/file/location/of/file.csv',
+ parse_params: {
+ col_sep: ',',
+ },
+ try: true,
+ )
+
+ result = TextModule.csv_import(
+ file: '/Users/me/Downloads/Textbausteine_final.csv',
+ parse_params: {
+ col_sep: ',',
+ },
+ try: false,
+ )
+
+returns
+
+ {
+ records: [record1, ...]
+ try: true, # true|false
+ success: true, # true|false
+ }
+
+=end
+
+ def csv_import(data)
+
+ if data[:file].present?
+ raise Exceptions::UnprocessableEntity, "No such file '#{data[:file]}'" if !File.exist?(data[:file])
+ begin
+ file = File.open(data[:file], 'r:UTF-8')
+ data[:string] = file.read
+ rescue => e
+ raise Exceptions::UnprocessableEntity, "Unable to read file '#{data[:file]}': #{e.inspect}"
+ end
+ end
+ if data[:string].blank?
+ raise Exceptions::UnprocessableEntity, 'Unable to parse empty file/string!'
+ 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!'
+ end
+ header.each do |item|
+ if item.respond_to?(:strip!)
+ item.strip!
+ end
+ next if !item.respond_to?(:downcase!)
+ item.downcase!
+ end
+
+ # get payload based on csv
+ payload = []
+ rows.each do |row|
+ if row[0].blank? && row[1].blank?
+ payload_last = payload.last
+ row.each_with_index do |item, count|
+ next if item.blank?
+ if payload_last[header[count].to_sym].class != Array
+ payload_last[header[count].to_sym] = [payload_last[header[count].to_sym]]
+ end
+ payload_last[header[count].to_sym].push item.strip
+ end
+ next
+ end
+ attributes = {}
+ row.each_with_index do |item, count|
+ next if !item
+ next if header[count].blank?
+ next if @csv_attributes_ignored&.include?(header[count].to_sym)
+ attributes[header[count].to_sym] = if item.respond_to?(:strip)
+ item.strip
+ else
+ item
+ end
+ end
+ data[:fixed_params]&.each do |key, value|
+ attributes[key] = value
+ end
+ payload.push attributes
+ end
+
+ # create or update records
+ csv_object_ids_ignored = @csv_object_ids_ignored || []
+ records = []
+ stats = {
+ created: 0,
+ updated: 0,
+ }
+ errors = []
+ line_count = 0
+ payload.each do |attributes|
+ line_count += 1
+ record = nil
+ %i[id number name login email].each do |lookup_by|
+ next if !attributes[lookup_by]
+ params = {}
+ params[lookup_by] = attributes[lookup_by]
+ record = lookup(params)
+ break if record
+ end
+
+ if attributes[:id].present? && !record
+ errors.push "Line #{line_count}: unknown record with id '#{attributes[:id]}' for #{new.class}."
+ next
+ end
+
+ if record && csv_object_ids_ignored.include?(record.id)
+ errors.push "Line #{line_count}: unable to update record with id '#{attributes[:id]}' for #{new.class}."
+ next
+ end
+
+ begin
+ clean_params = association_name_to_id_convert(attributes)
+ rescue => e
+ errors.push "Line #{line_count}: #{e.message}"
+ next
+ end
+
+ # create object
+ Transaction.execute(disable_notification: true, reset_user_id: true) do
+ UserInfo.current_user_id = clean_params[:updated_by_id] || clean_params[:created_by_id]
+ if !record
+ stats[:created] += 1
+ begin
+ csv_verify_attributes(clean_params)
+ clean_params = param_cleanup(clean_params)
+
+ if !UserInfo.current_user_id
+ clean_params[:created_by_id] = 1
+ clean_params[:updated_by_id] = 1
+ end
+ record = new(clean_params)
+ next if data[:try] == 'true' || data[:try] == true
+ record.associations_from_param(attributes)
+ record.save!
+ rescue => e
+ errors.push "Line #{line_count}: #{e.message}"
+ next
+ end
+ else
+ stats[:updated] += 1
+ next if data[:try] == 'true' || data[:try] == true
+ begin
+ csv_verify_attributes(clean_params)
+ clean_params = param_cleanup(clean_params)
+
+ if !UserInfo.current_user_id
+ clean_params[:updated_by_id] = 1
+ end
+
+ record.with_lock do
+ record.associations_from_param(attributes)
+ record.update_attributes!(clean_params)
+ end
+ rescue => e
+ errors.push "Line #{line_count}: #{e.message}"
+ next
+ end
+ end
+ end
+
+ records.push record
+ end
+
+ result = 'success'
+ if errors.present?
+ result = 'failed'
+ end
+
+ {
+ stats: stats,
+ records: records,
+ errors: errors,
+ try: data[:try],
+ result: result,
+ }
+
+ end
+
+=begin
+
+verify if attributes are valid, will raise an ArgumentError with "unknown attribute '#{key}' for #{new.class}."
+
+ Model.csv_verify_attributes({'attribute': 'some value'})
+
+=end
+
+ def csv_verify_attributes(clean_params)
+ all_clean_attributes = {}
+ new.attributes.each_key do |attribute|
+ all_clean_attributes[attribute.to_sym] = true
+ end
+ reflect_on_all_associations.map do |assoc|
+ all_clean_attributes[assoc.name.to_sym] = true
+ ref = if assoc.name.to_s.end_with?('_id')
+ "#{assoc.name}_id"
+ else
+ "#{assoc.name.to_s.chop}_ids"
+ end
+ all_clean_attributes[ref.to_sym] = true
+ end
+ clean_params.each_key do |key|
+ next if all_clean_attributes.key?(key.to_sym)
+ raise ArgumentError, "unknown attribute '#{key}' for #{new.class}."
+ end
+ true
+ end
+
+=begin
+
+ csv_string = Model.csv_example(
+ col_sep: ',',
+ )
+
+returns
+
+ csv_string
+
+=end
+
+ def csv_example(params = {})
+ header = []
+ csv_object_ids_ignored = @csv_object_ids_ignored || []
+ records = where.not(id: csv_object_ids_ignored).offset(1).limit(23).to_a
+ if records.count < 20
+ record_ids = records.pluck(:id).concat(csv_object_ids_ignored)
+ local_records = where.not(id: record_ids).limit(20 - records.count)
+ records = records.concat(local_records)
+ end
+ records_attributes_with_association_names = []
+ records.each do |record|
+ record_attributes_with_association_names = record.attributes_with_association_names
+ records_attributes_with_association_names.push record_attributes_with_association_names
+ record_attributes_with_association_names.each do |key, value|
+ next if value.class == ActiveSupport::HashWithIndifferentAccess
+ next if value.class == Hash
+ next if @csv_attributes_ignored&.include?(key.to_sym)
+ next if key.match?(/_id$/)
+ next if key.match?(/_ids$/)
+ next if key == 'created_by'
+ next if key == 'updated_by'
+ next if key == 'created_at'
+ next if key == 'updated_at'
+ next if header.include?(key)
+ header.push key
+ end
+ end
+
+ rows = []
+ records_attributes_with_association_names.each do |record|
+ row = []
+ rows_to_add = []
+ position = -1
+ header.each do |key|
+ position += 1
+ if record[key].class == ActiveSupport::TimeWithZone
+ row.push record[key].iso8601
+ next
+ end
+ if record[key].class == Array
+ entry_count = -2
+ record[key].each do |entry|
+ entry_count += 1
+ next if entry_count == -1
+ if !rows_to_add[entry_count]
+ rows_to_add[entry_count] = Array.new(header.count + 1) { '' }
+ end
+ rows_to_add[entry_count][position] = entry
+ end
+ record[key] = record[key][0]
+ end
+ row.push record[key]
+ end
+ rows.push row
+ next unless rows_to_add.count.positive?
+ rows_to_add.each do |item|
+ rows.push item
+ end
+ rows_to_add = []
+ end
+ CSV.generate(params) do |csv|
+ csv << header
+ rows.each do |row|
+ csv << row
+ end
+ end
+ end
+
+=begin
+
+serve methode to ignore model based on id
+
+class Model < ApplicationModel
+ include CanCsvImport
+ csv_object_ids_ignored(1, 2, 3)
+end
+
+=end
+
+ def csv_object_ids_ignored(*object_ids)
+ @csv_object_ids_ignored = object_ids
+ end
+
+=begin
+
+serve methode to ignore model attributes
+
+class Model < ApplicationModel
+ include CanCsvImport
+ csv_attributes_ignored :password,
+ :image_source,
+ :login_failed,
+ :source,
+ :image_source,
+ :image,
+ :authorizations,
+ :organizations
+
+end
+
+=end
+
+ def csv_attributes_ignored(*attributes)
+ @csv_attributes_ignored = attributes
+ end
+
+ end
+end
diff --git a/app/models/organization.rb b/app/models/organization.rb
index 967e72558..93833ec63 100644
--- a/app/models/organization.rb
+++ b/app/models/organization.rb
@@ -6,6 +6,7 @@ class Organization < ApplicationModel
include ChecksLatestChangeObserved
include HasHistory
include HasSearchIndexBackend
+ include CanCsvImport
include Organization::ChecksAccess
load 'organization/assets.rb'
diff --git a/app/models/text_module.rb b/app/models/text_module.rb
index 1cec59992..2b987c7d7 100644
--- a/app/models/text_module.rb
+++ b/app/models/text_module.rb
@@ -3,10 +3,14 @@
class TextModule < ApplicationModel
include ChecksClientNotification
include ChecksHtmlSanitized
+ include CanCsvImport
validates :name, presence: true
validates :content, presence: true
+ before_create :validate_content
+ before_update :validate_content
+
sanitized_html :content
=begin
@@ -97,4 +101,14 @@ push text_modules to online
true
end
+ private
+
+ def validate_content
+ return true if content.blank?
+ return true if content.match?(/<.+?>/)
+ content.gsub!(/(\r\n|\n\r|\r)/, "\n")
+ self.content = content.text2html
+ true
+ end
+
end
diff --git a/app/models/ticket.rb b/app/models/ticket.rb
index b1a40c442..7007880cb 100644
--- a/app/models/ticket.rb
+++ b/app/models/ticket.rb
@@ -4,6 +4,7 @@ class Ticket < ApplicationModel
include HasActivityStreamLog
include ChecksClientNotification
include ChecksLatestChangeObserved
+ include CanCsvImport
include HasHistory
include HasTags
include HasSearchIndexBackend
diff --git a/app/models/ticket/article.rb b/app/models/ticket/article.rb
index 1067a3793..187472085 100644
--- a/app/models/ticket/article.rb
+++ b/app/models/ticket/article.rb
@@ -4,6 +4,7 @@ class Ticket::Article < ApplicationModel
include ChecksClientNotification
include HasHistory
include ChecksHtmlSanitized
+ include CanCsvImport
include Ticket::Article::ChecksAccess
load 'ticket/article/assets.rb'
diff --git a/app/models/user.rb b/app/models/user.rb
index d7a6271a3..c3f6e0d7c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -28,6 +28,7 @@ class User < ApplicationModel
include ChecksClientNotification
include HasHistory
include HasSearchIndexBackend
+ include CanCsvImport
include HasGroups
include HasRoles
include User::ChecksAccess
@@ -74,6 +75,18 @@ class User < ApplicationModel
:source,
:login_failed
+ csv_object_ids_ignored 1
+
+ csv_attributes_ignored :password,
+ :login_failed,
+ :source,
+ :image_source,
+ :image,
+ :authorizations,
+ :organizations,
+ :groups,
+ :user_groups
+
def ignore_search_indexing?(_action)
# ignore internal user
return true if id == 1
diff --git a/config/routes/organization.rb b/config/routes/organization.rb
index 930a6c393..b1995c5cc 100644
--- a/config/routes/organization.rb
+++ b/config/routes/organization.rb
@@ -2,12 +2,14 @@ Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path
# organizations
- match api_path + '/organizations/search', to: 'organizations#search', via: %i[get post]
- match api_path + '/organizations', to: 'organizations#index', via: :get
- match api_path + '/organizations/:id', to: 'organizations#show', via: :get
- match api_path + '/organizations', to: 'organizations#create', via: :post
- match api_path + '/organizations/:id', to: 'organizations#update', via: :put
- match api_path + '/organizations/:id', to: 'organizations#destroy', via: :delete
- match api_path + '/organizations/history/:id', to: 'organizations#history', via: :get
+ match api_path + '/organizations/import_example', to: 'organizations#import_example', via: :get
+ match api_path + '/organizations/import', to: 'organizations#import_start', via: :post
+ match api_path + '/organizations/search', to: 'organizations#search', via: %i[get post]
+ match api_path + '/organizations', to: 'organizations#index', via: :get
+ match api_path + '/organizations/:id', to: 'organizations#show', via: :get
+ match api_path + '/organizations', to: 'organizations#create', via: :post
+ match api_path + '/organizations/:id', to: 'organizations#update', via: :put
+ match api_path + '/organizations/:id', to: 'organizations#destroy', via: :delete
+ match api_path + '/organizations/history/:id', to: 'organizations#history', via: :get
end
diff --git a/config/routes/text_module.rb b/config/routes/text_module.rb
index 821b985dd..f1813feaf 100644
--- a/config/routes/text_module.rb
+++ b/config/routes/text_module.rb
@@ -2,10 +2,12 @@ Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path
# text_modules
- match api_path + '/text_modules', to: 'text_modules#index', via: :get
- match api_path + '/text_modules/:id', to: 'text_modules#show', via: :get
- match api_path + '/text_modules', to: 'text_modules#create', via: :post
- match api_path + '/text_modules/:id', to: 'text_modules#update', via: :put
- match api_path + '/text_modules/:id', to: 'text_modules#destroy', via: :delete
+ match api_path + '/text_modules/import_example', to: 'text_modules#import_example', via: :get
+ match api_path + '/text_modules/import', to: 'text_modules#import_start', via: :post
+ match api_path + '/text_modules', to: 'text_modules#index', via: :get
+ match api_path + '/text_modules/:id', to: 'text_modules#show', via: :get
+ match api_path + '/text_modules', to: 'text_modules#create', via: :post
+ match api_path + '/text_modules/:id', to: 'text_modules#update', via: :put
+ match api_path + '/text_modules/:id', to: 'text_modules#destroy', via: :delete
end
diff --git a/config/routes/user.rb b/config/routes/user.rb
index 7847d30e3..c453d4e15 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -2,7 +2,7 @@ Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path
# users
- match api_path + '/users/search', to: 'users#search', via: %i[get post]
+ match api_path + '/users/search', to: 'users#search', via: %i[get post option]
match api_path + '/users/recent', to: 'users#recent', via: %i[get post]
match api_path + '/users/password_reset', to: 'users#password_reset_send', via: :post
match api_path + '/users/password_reset_verify', to: 'users#password_reset_verify', via: :post
@@ -11,6 +11,9 @@ Zammad::Application.routes.draw do
match api_path + '/users/out_of_office', to: 'users#out_of_office', via: :put
match api_path + '/users/account', to: 'users#account_remove', via: :delete
+ match api_path + '/users/import_example', to: 'users#import_example', via: :get
+ match api_path + '/users/import', to: 'users#import_start', via: :post
+
match api_path + '/users/avatar', to: 'users#avatar_new', via: :post
match api_path + '/users/avatar', to: 'users#avatar_list', via: :get
match api_path + '/users/avatar', to: 'users#avatar_destroy', via: :delete
diff --git a/test/controllers/organization_controller_test.rb b/test/controllers/organization_controller_test.rb
index 1ba5f2849..67e844c06 100644
--- a/test/controllers/organization_controller_test.rb
+++ b/test/controllers/organization_controller_test.rb
@@ -14,17 +14,6 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
UserInfo.current_user_id = 1
- @backup_admin = User.create_or_update(
- login: 'backup-admin',
- firstname: 'Backup',
- lastname: 'Agent',
- email: 'backup-admin@example.com',
- password: 'adminpw',
- active: true,
- roles: roles,
- groups: groups,
- )
-
@admin = User.create_or_update(
login: 'rest-admin',
firstname: 'Rest',
@@ -510,4 +499,110 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
end
+ test '05.01 csv example - customer no access' do
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-customer1@example.com', 'customer1pw')
+
+ get '/api/v1/organizations/import_example', params: {}, headers: @headers.merge('Authorization' => credentials)
+ assert_response(401)
+ result = JSON.parse(@response.body)
+ assert_equal('Not authorized (user)!', result['error'])
+ end
+
+ test '05.02 csv example - admin access' do
+
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
+
+ get '/api/v1/organizations/import_example', params: {}, headers: @headers.merge('Authorization' => credentials)
+ assert_response(200)
+
+ rows = CSV.parse(@response.body)
+ header = rows.shift
+
+ assert_equal('id', header[0])
+ assert_equal('name', header[1])
+ assert_equal('shared', header[2])
+ assert_equal('domain', header[3])
+ assert_equal('domain_assignment', header[4])
+ assert_equal('active', header[5])
+ assert_equal('note', header[6])
+ assert(header.include?('members'))
+ end
+
+ test '05.03 csv import - admin access' do
+
+ UserInfo.current_user_id = 1
+ customer1 = User.create_or_update(
+ login: 'customer1-members@example.com',
+ firstname: 'Member',
+ lastname: 'Customer',
+ email: 'customer1-members@example.com',
+ password: 'customerpw',
+ active: true,
+ )
+ customer2 = User.create_or_update(
+ login: 'customer2-members@example.com',
+ firstname: 'Member',
+ lastname: 'Customer',
+ email: 'customer2-members@example.com',
+ password: 'customerpw',
+ active: true,
+ )
+ UserInfo.current_user_id = nil
+
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_equal('true', result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('failed', result['result'])
+ assert_equal(2, result['errors'].count)
+ assert_equal("Line 1: unknown attribute 'name2' for Organization.", result['errors'][0])
+ assert_equal("Line 2: unknown attribute 'name2' for Organization.", result['errors'][1])
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_equal('true', result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('success', result['result'])
+
+ assert_nil(Organization.find_by(name: 'organization-member-import1'))
+ assert_nil(Organization.find_by(name: 'organization-member-import2'))
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_nil(result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('success', result['result'])
+
+ organization1 = Organization.find_by(name: 'organization-member-import1')
+ assert(organization1)
+ assert_equal(organization1.name, 'organization-member-import1')
+ assert_equal(organization1.members.count, 1)
+ assert_equal(organization1.members.first.login, customer1.login)
+ assert_equal(organization1.active, true)
+ organization2 = Organization.find_by(name: 'organization-member-import2')
+ assert(organization2)
+ assert_equal(organization2.name, 'organization-member-import2')
+ assert_equal(organization2.members.count, 1)
+ assert_equal(organization2.members.first.login, customer2.login)
+ assert_equal(organization2.active, false)
+
+ end
+
end
diff --git a/test/controllers/text_module_controller_test.rb b/test/controllers/text_module_controller_test.rb
new file mode 100644
index 000000000..077a18261
--- /dev/null
+++ b/test/controllers/text_module_controller_test.rb
@@ -0,0 +1,157 @@
+
+require 'test_helper'
+require 'rake'
+
+class TextModuleControllerTest < ActionDispatch::IntegrationTest
+ setup do
+
+ # set accept header
+ @headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
+
+ # create agent
+ roles = Role.where(name: %w[Admin Agent])
+ groups = Group.all
+
+ UserInfo.current_user_id = 1
+
+ @admin = User.create_or_update(
+ login: 'rest-admin',
+ firstname: 'Rest',
+ lastname: 'Agent',
+ email: 'rest-admin@example.com',
+ password: 'adminpw',
+ active: true,
+ roles: roles,
+ groups: groups,
+ )
+
+ # create agent
+ roles = Role.where(name: 'Agent')
+ @agent = User.create_or_update(
+ login: 'rest-agent@example.com',
+ firstname: 'Rest',
+ lastname: 'Agent',
+ email: 'rest-agent@example.com',
+ password: 'agentpw',
+ active: true,
+ roles: roles,
+ groups: groups,
+ )
+
+ # create customer without org
+ roles = Role.where(name: 'Customer')
+ @customer_without_org = User.create_or_update(
+ login: 'rest-customer1@example.com',
+ firstname: 'Rest',
+ lastname: 'Customer1',
+ email: 'rest-customer1@example.com',
+ password: 'customer1pw',
+ active: true,
+ roles: roles,
+ )
+
+ # create customer
+ @customer_with_org = User.create_or_update(
+ login: 'rest-customer2@example.com',
+ firstname: 'Rest',
+ lastname: 'Customer2',
+ email: 'rest-customer2@example.com',
+ password: 'customer2pw',
+ active: true,
+ roles: roles,
+ )
+
+ UserInfo.current_user_id = nil
+ end
+
+ test '05.01 csv example - customer no access' do
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-customer1@example.com', 'customer1pw')
+
+ get '/api/v1/text_modules/import_example', params: {}, headers: @headers.merge('Authorization' => credentials)
+ assert_response(401)
+ result = JSON.parse(@response.body)
+ assert_equal('Not authorized (user)!', result['error'])
+ end
+
+ test '05.02 csv example - admin access' do
+ TextModule.load('en-en')
+
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
+
+ get '/api/v1/text_modules/import_example', params: {}, headers: @headers.merge('Authorization' => credentials)
+ assert_response(200)
+ rows = CSV.parse(@response.body)
+ header = rows.shift
+
+ assert_equal('id', header[0])
+ assert_equal('name', header[1])
+ assert_equal('keywords', header[2])
+ assert_equal('content', header[3])
+ assert_equal('note', header[4])
+ assert_equal('active', header[5])
+ assert_not(header.include?('organization'))
+ assert_not(header.include?('priority'))
+ assert_not(header.include?('state'))
+ assert_not(header.include?('owner'))
+ assert_not(header.include?('customer'))
+ end
+
+ test '05.03 csv import - admin access' do
+
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_equal('true', result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('failed', result['result'])
+ assert_equal(2, result['errors'].count)
+ assert_equal("Line 1: unknown attribute 'keywords2' for TextModule.", result['errors'][0])
+ assert_equal("Line 2: unknown attribute 'keywords2' for TextModule.", result['errors'][1])
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_equal('true', result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('success', result['result'])
+
+ assert_nil(TextModule.find_by(name: 'some name1'))
+ assert_nil(TextModule.find_by(name: 'some name2'))
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_nil(result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('success', result['result'])
+
+ text_module1 = TextModule.find_by(name: 'some name1')
+ assert(text_module1)
+ assert_equal(text_module1.name, 'some name1')
+ assert_equal(text_module1.keywords, 'keyword1')
+ assert_equal(text_module1.content, 'some
content1')
+ assert_equal(text_module1.active, true)
+ text_module2 = TextModule.find_by(name: 'some name2')
+ assert(text_module2)
+ assert_equal(text_module2.name, 'some name2')
+ assert_equal(text_module2.keywords, 'keyword2')
+ assert_equal(text_module2.content, 'some content
test123')
+ assert_equal(text_module2.active, true)
+
+ end
+
+end
diff --git a/test/controllers/user_controller_test.rb b/test/controllers/user_controller_test.rb
index 1ff94a176..d6a365bb7 100644
--- a/test/controllers/user_controller_test.rb
+++ b/test/controllers/user_controller_test.rb
@@ -957,4 +957,93 @@ class UserControllerTest < ActionDispatch::IntegrationTest
end
+ test '05.01 csv example - customer no access' do
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-customer1@example.com', 'customer1pw')
+
+ get '/api/v1/users/import_example', params: {}, headers: @headers.merge('Authorization' => credentials)
+ assert_response(401)
+ result = JSON.parse(@response.body)
+ assert_equal('Not authorized (user)!', result['error'])
+ end
+
+ test '05.02 csv example - admin access' do
+
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
+
+ get '/api/v1/users/import_example', params: {}, headers: @headers.merge('Authorization' => credentials)
+ assert_response(200)
+
+ rows = CSV.parse(@response.body)
+ header = rows.shift
+
+ assert_equal('id', header[0])
+ assert_equal('login', header[1])
+ assert_equal('firstname', header[2])
+ assert_equal('lastname', header[3])
+ assert_equal('email', header[4])
+ assert(header.include?('organization'))
+ end
+
+ test '05.03 csv import - admin access' do
+
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_equal('true', result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('failed', result['result'])
+ assert_equal(2, result['errors'].count)
+ assert_equal("Line 1: unknown attribute 'firstname2' for User.", result['errors'][0])
+ assert_equal("Line 2: unknown attribute 'firstname2' for User.", result['errors'][1])
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_equal('true', result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('success', result['result'])
+
+ assert_nil(User.find_by(login: 'user-simple-import1'))
+ assert_nil(User.find_by(login: 'user-simple-import2'))
+
+ # 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 }
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(Hash, result.class)
+
+ assert_nil(result['try'])
+ assert_equal(2, result['records'].count)
+ assert_equal('success', result['result'])
+
+ user1 = User.find_by(login: 'user-simple-import1')
+ assert(user1)
+ assert_equal(user1.login, 'user-simple-import1')
+ assert_equal(user1.firstname, 'firstname-simple-import1')
+ assert_equal(user1.lastname, 'lastname-simple-import1')
+ assert_equal(user1.email, 'user-simple-import1@example.com')
+ assert_equal(user1.active, true)
+ user2 = User.find_by(login: 'user-simple-import2')
+ assert(user2)
+ assert_equal(user2.login, 'user-simple-import2')
+ assert_equal(user2.firstname, 'firstname-simple-import2')
+ assert_equal(user2.lastname, 'lastname-simple-import2')
+ assert_equal(user2.email, 'user-simple-import2@example.com')
+ assert_equal(user2.active, false)
+
+ user1.destroy!
+ user2.destroy!
+ end
+
end
diff --git a/test/fixtures/csv/organization_simple.csv b/test/fixtures/csv/organization_simple.csv
new file mode 100644
index 000000000..3874c91d7
--- /dev/null
+++ b/test/fixtures/csv/organization_simple.csv
@@ -0,0 +1,4 @@
+id;name;members;active
+;organization-member-import1;customer1-members@example.com
+;organization-member-import2;customer2-members@example.com;false
+
diff --git a/test/fixtures/csv/organization_simple_col_not_existing.csv b/test/fixtures/csv/organization_simple_col_not_existing.csv
new file mode 100644
index 000000000..2d3953e81
--- /dev/null
+++ b/test/fixtures/csv/organization_simple_col_not_existing.csv
@@ -0,0 +1,3 @@
+id;name2;shared;domain;domain_assignment;active;note
+;org-simple-import1;true;org-simple-import1.example.com;false;true;some note1
+;org-simple-import2;true;org-simple-import2.example.com;false;false;some note2
diff --git a/test/fixtures/csv/text_module_simple.csv b/test/fixtures/csv/text_module_simple.csv
new file mode 100644
index 000000000..cb50d27b2
--- /dev/null
+++ b/test/fixtures/csv/text_module_simple.csv
@@ -0,0 +1,4 @@
+name;keywords;content;note;active;
+some name1;keyword1;"some
+content1";-;
+some name2;keyword2;some content
test123
\ No newline at end of file
diff --git a/test/fixtures/csv/text_module_simple_col_not_existing.csv b/test/fixtures/csv/text_module_simple_col_not_existing.csv
new file mode 100644
index 000000000..1ad78437d
--- /dev/null
+++ b/test/fixtures/csv/text_module_simple_col_not_existing.csv
@@ -0,0 +1,3 @@
+name;keywords2;content;note;active;
+some name1;keyword1;"some content1";-;
+some name2;keyword2;some content
test123
\ No newline at end of file
diff --git a/test/fixtures/csv/user_simple.csv b/test/fixtures/csv/user_simple.csv
new file mode 100644
index 000000000..b90294703
--- /dev/null
+++ b/test/fixtures/csv/user_simple.csv
@@ -0,0 +1,3 @@
+login;firstname;lastname;email;active;
+user-simple-import1;firstname-simple-import1;lastname-simple-import1;user-simple-import1@example.com;true
+user-simple-import2;firstname-simple-import2;lastname-simple-import2;user-simple-import2@example.com;false
diff --git a/test/fixtures/csv/user_simple_col_not_existing.csv b/test/fixtures/csv/user_simple_col_not_existing.csv
new file mode 100644
index 000000000..d112b5932
--- /dev/null
+++ b/test/fixtures/csv/user_simple_col_not_existing.csv
@@ -0,0 +1,3 @@
+login;firstname2;lastname;email;active;
+user-simple-import1;firstname-simple-import1;lastname-simple-import1;user-simple-import1@example.com;true
+user-simple-import2;firstname-simple-import2;lastname-simple-import2;user-simple-import2@example.com;false
diff --git a/test/unit/organization_csv_import_test.rb b/test/unit/organization_csv_import_test.rb
new file mode 100644
index 000000000..9979dd435
--- /dev/null
+++ b/test/unit/organization_csv_import_test.rb
@@ -0,0 +1,209 @@
+
+require 'test_helper'
+
+class OrganizationCsvImportTest < ActiveSupport::TestCase
+
+ test 'import example verify' do
+ csv_string = Organization.csv_example
+
+ rows = CSV.parse(csv_string)
+ header = rows.shift
+ assert_equal('id', header[0])
+ assert_equal('name', header[1])
+ assert_equal('shared', header[2])
+ assert_equal('domain', header[3])
+ assert_equal('domain_assignment', header[4])
+ assert_equal('active', header[5])
+ assert_equal('note', header[6])
+ assert(header.include?('members'))
+ 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"
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(Organization.find_by(name: 'org-simple-import1'))
+ assert_nil(Organization.find_by(name: 'org-simple-import2'))
+
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ organization1 = Organization.find_by(name: 'org-simple-import1')
+ assert(organization1)
+ assert_equal(organization1.name, 'org-simple-import1')
+ assert_equal(organization1.shared, true)
+ assert_equal(organization1.domain, 'org-simple-import1.example.com')
+ assert_equal(organization1.domain_assignment, false)
+ assert_equal(organization1.note, 'some note1')
+ assert_equal(organization1.active, true)
+ organization2 = Organization.find_by(name: 'org-simple-import2')
+ assert(organization2)
+ assert_equal(organization2.name, 'org-simple-import2')
+ assert_equal(organization2.shared, true)
+ assert_equal(organization2.domain, 'org-simple-import2.example.com')
+ assert_equal(organization2.domain_assignment, false)
+ assert_equal(organization2.note, 'some note2')
+ assert_equal(organization2.active, false)
+
+ organization1.destroy!
+ organization2.destroy!
+ end
+
+ test 'simple import with invalid id' do
+
+ csv_string = "id;name;shared;domain;domain_assignment;active;note;\n999999999;organization-simple-invalid_id-import1;\n;organization-simple-invalid_id-import2;\n"
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(1, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown record with id '999999999' for Organization.", result[:errors][0])
+
+ assert_nil(Organization.find_by(name: 'organization-simple-invalid_id-import1'))
+ assert_nil(Organization.find_by(name: 'organization-simple-invalid_id-import2'))
+
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(1, result[:records].count)
+ assert_equal('failed', result[:result])
+
+ assert_nil(Organization.find_by(name: 'organization-simple-invalid_id-import1'))
+
+ organization2 = Organization.find_by(name: 'organization-simple-invalid_id-import2')
+ assert(organization2)
+ assert_equal(organization2.name, 'organization-simple-invalid_id-import2')
+ assert_equal(organization2.active, true)
+
+ organization2.destroy!
+ end
+
+ test 'simple import with members' do
+ UserInfo.current_user_id = 1
+
+ name = rand(999_999_999)
+ customer1 = User.create_or_update(
+ login: "customer1-members#{name}@example.com",
+ firstname: 'Member',
+ lastname: "Customer#{name}",
+ email: "customer1-members#{name}@example.com",
+ password: 'customerpw',
+ active: true,
+ )
+ customer2 = User.create_or_update(
+ login: "customer2-members#{name}@example.com",
+ firstname: 'Member',
+ lastname: "Customer#{name}",
+ email: "customer2-members#{name}@example.com",
+ password: 'customerpw',
+ active: true,
+ )
+
+ csv_string = "id;name;members;\n;organization-member-import1;\n;organization-member-import2;#{customer1.email}\n;;#{customer2.email}"
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(Organization.find_by(name: 'organization-member-import1'))
+ assert_nil(Organization.find_by(name: 'organization-member-import2'))
+
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ organization1 = Organization.find_by(name: 'organization-member-import1')
+ assert(organization1)
+ assert_equal(organization1.name, 'organization-member-import1')
+ assert_equal(organization1.members.count, 0)
+ organization2 = Organization.find_by(name: 'organization-member-import2')
+ assert(organization2)
+ assert_equal(organization2.name, 'organization-member-import2')
+ assert_equal(organization2.members.count, 2)
+
+ customer1.destroy!
+ customer2.destroy!
+ organization1.destroy!
+ organization2.destroy!
+ end
+
+ test 'invalid attributes' do
+
+ csv_string = "name;note;not existing\norganization-invalid-import1;some note;abc\norganization-invalid-import2;some other note;123; with not exsiting header\n"
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown attribute 'not existing' for Organization.", result[:errors][0])
+ assert_equal("Line 2: unknown attribute 'not existing' for Organization.", result[:errors][1])
+
+ assert_nil(Organization.find_by(name: 'organization-invalid-import1'))
+ assert_nil(Organization.find_by(name: 'organization-invalid-import2'))
+
+ result = Organization.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown attribute 'not existing' for Organization.", result[:errors][0])
+ assert_equal("Line 2: unknown attribute 'not existing' for Organization.", result[:errors][1])
+
+ assert_nil(Organization.find_by(name: 'organization-invalid-import1'))
+ assert_nil(Organization.find_by(name: 'organization-invalid-import2'))
+ end
+
+end
diff --git a/test/unit/text_module_csv_import_test.rb b/test/unit/text_module_csv_import_test.rb
new file mode 100644
index 000000000..af0208284
--- /dev/null
+++ b/test/unit/text_module_csv_import_test.rb
@@ -0,0 +1,72 @@
+
+require 'test_helper'
+
+class TextModuleCsvImportTest < ActiveSupport::TestCase
+
+ test 'import example verify' do
+ TextModule.load('en-en')
+ csv_string = TextModule.csv_example
+
+ rows = CSV.parse(csv_string)
+ header = rows.shift
+ assert_equal('id', header[0])
+ assert_equal('name', header[1])
+ assert_equal('keywords', header[2])
+ assert_equal('content', header[3])
+ assert_equal('note', header[4])
+ assert_equal('active', header[5])
+ assert_not(header.include?('organization'))
+ assert_not(header.include?('priority'))
+ assert_not(header.include?('state'))
+ assert_not(header.include?('owner'))
+ assert_not(header.include?('customer'))
+ end
+
+ test 'simple import' do
+
+ csv_string = "name;keywords;content;note;active;\nsome name1;keyword1;\"some\ncontent1\";-;\nsome name2;keyword2;some content
test123\n"
+ result = TextModule.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(TextModule.find_by(name: 'some name1'))
+ assert_nil(TextModule.find_by(name: 'some name2'))
+
+ result = TextModule.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ text_module1 = TextModule.find_by(name: 'some name1')
+ assert(text_module1)
+ assert_equal(text_module1.name, 'some name1')
+ assert_equal(text_module1.keywords, 'keyword1')
+ assert_equal(text_module1.content, 'some
content1')
+ assert_equal(text_module1.active, true)
+ text_module2 = TextModule.find_by(name: 'some name2')
+ assert(text_module2)
+ assert_equal(text_module2.name, 'some name2')
+ assert_equal(text_module2.keywords, 'keyword2')
+ assert_equal(text_module2.content, 'some content
test123')
+ assert_equal(text_module2.active, true)
+
+ text_module1.destroy!
+ text_module2.destroy!
+ end
+
+end
diff --git a/test/unit/ticket_csv_import_test.rb b/test/unit/ticket_csv_import_test.rb
new file mode 100644
index 000000000..fdd7ce8d8
--- /dev/null
+++ b/test/unit/ticket_csv_import_test.rb
@@ -0,0 +1,172 @@
+
+require 'test_helper'
+
+class TicketCsvImportTest < ActiveSupport::TestCase
+
+ test 'import example verify' do
+ csv_string = Ticket.csv_example
+
+ rows = CSV.parse(csv_string)
+ header = rows.shift
+ assert_equal('id', header[0])
+ assert_equal('number', header[1])
+ assert_equal('title', header[2])
+ assert_equal('note', header[3])
+ assert_equal('first_response_at', header[4])
+ assert_equal('first_response_escalation_at', header[5])
+ assert(header.include?('organization'))
+ assert(header.include?('priority'))
+ assert(header.include?('state'))
+ assert(header.include?('owner'))
+ assert(header.include?('customer'))
+
+ 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"
+ result = Ticket.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(Ticket.find_by(number: '123456'))
+ assert_nil(Ticket.find_by(number: '123457'))
+
+ result = Ticket.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ ticket1 = Ticket.find_by(number: '123456')
+ assert(ticket1)
+ assert_equal(ticket1.number, '123456')
+ assert_equal(ticket1.title, 'some title1')
+ assert_equal(ticket1.state.name, 'new')
+ assert_equal(ticket1.priority.name, '2 normal')
+ assert_equal(ticket1.owner.login, '-')
+ assert_equal(ticket1.customer.login, 'nicole.braun@zammad.org')
+ assert_equal(ticket1.note, 'some note1')
+ ticket2 = Ticket.find_by(number: '123457')
+ assert(ticket2)
+ assert_equal(ticket2.number, '123457')
+ assert_equal(ticket2.title, 'some title2')
+ assert_equal(ticket2.state.name, 'closed')
+ assert_equal(ticket2.priority.name, '1 low')
+ assert_equal(ticket2.owner.login, 'admin@example.com')
+ assert_equal(ticket2.customer.login, 'nicole.braun@zammad.org')
+ assert_equal(ticket2.note, 'some note2')
+
+ ticket1.destroy!
+ ticket2.destroy!
+ end
+
+ test 'simple import with invalid id' do
+
+ csv_string = "id;number;title;state;priority;owner;customer;group;note\n999999999;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"
+ result = Ticket.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(1, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown record with id '999999999' for Ticket.", result[:errors][0])
+
+ assert_nil(Ticket.find_by(number: '123456'))
+ assert_nil(Ticket.find_by(number: '123457'))
+
+ result = Ticket.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(1, result[:records].count)
+ assert_equal('failed', result[:result])
+
+ assert_nil(Ticket.find_by(number: '123456'))
+
+ ticket2 = Ticket.find_by(number: '123457')
+ assert(ticket2)
+ assert_equal(ticket2.title, 'some title2')
+ assert_equal(ticket2.note, 'some note2')
+
+ csv_string = "id;number;title;state;priority;owner;customer;group;note\n999999999;123456;some title1;new;2 normal;-;nicole.braun@zammad.org;Users;some note1\n;123457;some title22;closed;1 low;admin@example.com;nicole.braun@zammad.org;Users;some note22\n"
+
+ result = Ticket.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(1, result[:records].count)
+ assert_equal('failed', result[:result])
+
+ assert_nil(Ticket.find_by(number: '123456'))
+
+ ticket2 = Ticket.find_by(number: '123457')
+ assert(ticket2)
+ assert_equal(ticket2.title, 'some title22')
+ assert_equal(ticket2.note, 'some note22')
+
+ ticket2.destroy!
+ end
+
+ test 'invalid attributes' do
+
+ csv_string = "id;number;not_existing;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"
+ result = Ticket.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown attribute 'not_existing' for Ticket.", result[:errors][0])
+ assert_equal("Line 2: unknown attribute 'not_existing' for Ticket.", result[:errors][1])
+
+ assert_nil(Ticket.find_by(number: '123456'))
+ assert_nil(Ticket.find_by(number: '123457'))
+
+ result = Ticket.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown attribute 'not_existing' for Ticket.", result[:errors][0])
+ assert_equal("Line 2: unknown attribute 'not_existing' for Ticket.", result[:errors][1])
+
+ assert_nil(Ticket.find_by(number: '123456'))
+ assert_nil(Ticket.find_by(number: '123457'))
+ end
+
+end
diff --git a/test/unit/user_csv_import_test.rb b/test/unit/user_csv_import_test.rb
new file mode 100644
index 000000000..c6d111b8d
--- /dev/null
+++ b/test/unit/user_csv_import_test.rb
@@ -0,0 +1,416 @@
+
+require 'test_helper'
+
+class UserCsvImportTest < ActiveSupport::TestCase
+
+ test 'import example verify' do
+ csv_string = User.csv_example
+
+ rows = CSV.parse(csv_string)
+ header = rows.shift
+
+ assert_equal('id', header[0])
+ assert_equal('login', header[1])
+ assert_equal('firstname', header[2])
+ assert_equal('lastname', header[3])
+ assert_equal('email', header[4])
+ assert(header.include?('organization'))
+ 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"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(User.find_by(login: 'user-simple-import1'))
+ assert_nil(User.find_by(login: 'user-simple-import2'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ user1 = User.find_by(login: 'user-simple-import1')
+ assert(user1)
+ assert_equal(user1.login, 'user-simple-import1')
+ assert_equal(user1.firstname, 'firstname-simple-import1')
+ assert_equal(user1.lastname, 'lastname-simple-import1')
+ assert_equal(user1.email, 'user-simple-import1@example.com')
+ assert_equal(user1.active, true)
+ user2 = User.find_by(login: 'user-simple-import2')
+ assert(user2)
+ assert_equal(user2.login, 'user-simple-import2')
+ assert_equal(user2.firstname, 'firstname-simple-import2')
+ assert_equal(user2.lastname, 'lastname-simple-import2')
+ assert_equal(user2.email, 'user-simple-import2@example.com')
+ assert_equal(user2.active, false)
+
+ user1.destroy!
+ user2.destroy!
+ end
+
+ test 'simple import with invalid id' do
+
+ csv_string = "id;login;firstname;lastname;email;active;\n999999999;user-simple-invalid_id-import1;firstname-simple-import1;lastname-simple-import1;user-simple-invalid_id-import1@example.com;true\n;user-simple-invalid_id-import2;firstname-simple-import2;lastname-simple-import2;user-simple-invalid_id-import2@example.com;false\n"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(1, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown record with id '999999999' for User.", result[:errors][0])
+
+ assert_nil(User.find_by(login: 'user-simple-invalid_id-import1'))
+ assert_nil(User.find_by(login: 'user-simple-invalid_id-import2'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(1, result[:records].count)
+ assert_equal('failed', result[:result])
+
+ assert_nil(User.find_by(login: 'user-simple-invalid_id-import1'))
+
+ user2 = User.find_by(login: 'user-simple-invalid_id-import2')
+ assert(user2)
+ assert_equal(user2.login, 'user-simple-invalid_id-import2')
+ assert_equal(user2.firstname, 'firstname-simple-import2')
+ assert_equal(user2.lastname, 'lastname-simple-import2')
+ assert_equal(user2.email, 'user-simple-invalid_id-import2@example.com')
+ assert_equal(user2.active, false)
+
+ user2.destroy!
+ end
+
+ test 'simple import with read only id' do
+
+ csv_string = "id;login;firstname;lastname;email;active;\n1;user-simple-readonly_id-import1;firstname-simple-import1;lastname-simple-import1;user-simple-readonly_id-import1@example.com;true\n;user-simple-readonly_id-import2;firstname-simple-import2;lastname-simple-import2;user-simple-readonly_id-import2@example.com;false\n"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(1, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unable to update record with id '1' for User.", result[:errors][0])
+
+ assert_nil(User.find_by(login: 'user-simple-readonly_id-import1'))
+ assert_nil(User.find_by(login: 'user-simple-readonly_id-import2'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(1, result[:records].count)
+ assert_equal('failed', result[:result])
+
+ assert_nil(User.find_by(login: 'user-simple-readonly_id-import1'))
+
+ user2 = User.find_by(login: 'user-simple-readonly_id-import2')
+ assert(user2)
+ assert_equal(user2.login, 'user-simple-readonly_id-import2')
+ assert_equal(user2.firstname, 'firstname-simple-import2')
+ assert_equal(user2.lastname, 'lastname-simple-import2')
+ assert_equal(user2.email, 'user-simple-readonly_id-import2@example.com')
+ assert_equal(user2.active, false)
+
+ user2.destroy!
+ end
+
+ test 'simple import with roles' do
+ UserInfo.current_user_id = 1
+
+ admin = User.create_or_update(
+ login: 'admin1@example.com',
+ firstname: 'Admin',
+ lastname: '1',
+ email: 'admin1@example.com',
+ password: 'agentpw',
+ active: true,
+ roles: Role.where(name: 'Admin'),
+ )
+
+ csv_string = "login;firstname;lastname;email;roles;\nuser-role-import1;firstname-role-import1;lastname-role-import1;user-role-import1@example.com;Customer;\nuser-role-import2;firstname-role-import2;lastname-role-import2;user-role-import2@example.com;Agent\n;;;;Admin"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(User.find_by(login: 'user-role-import1'))
+ assert_nil(User.find_by(login: 'user-role-import2'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ user1 = User.find_by(login: 'user-role-import1')
+ assert(user1)
+ assert_equal(user1.login, 'user-role-import1')
+ assert_equal(user1.firstname, 'firstname-role-import1')
+ assert_equal(user1.lastname, 'lastname-role-import1')
+ assert_equal(user1.email, 'user-role-import1@example.com')
+ assert_equal(user1.roles.count, 1)
+ user2 = User.find_by(login: 'user-role-import2')
+ assert(user2)
+ assert_equal(user2.login, 'user-role-import2')
+ assert_equal(user2.firstname, 'firstname-role-import2')
+ assert_equal(user2.lastname, 'lastname-role-import2')
+ assert_equal(user2.email, 'user-role-import2@example.com')
+ assert_equal(user2.roles.count, 2)
+
+ user1.destroy!
+ user2.destroy!
+ admin.destroy!
+ end
+
+ test 'simple import + fixed params' do
+
+ csv_string = "login;firstname;lastname;email\nuser-simple-import-fixed1;firstname-simple-import-fixed1;lastname-simple-import-fixed1;user-simple-import-fixed1@example.com\nuser-simple-import-fixed2;firstname-simple-import-fixed2;lastname-simple-import-fixed2;user-simple-import-fixed2@example.com\n"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ fixed_params: {
+ note: 'some note',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(User.find_by(login: 'user-simple-import-fixed1'))
+ assert_nil(User.find_by(login: 'user-simple-import-fixed2'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ fixed_params: {
+ note: 'some note',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:records].count)
+ assert_equal('success', result[:result])
+
+ user1 = User.find_by(login: 'user-simple-import-fixed1')
+ user2 = User.find_by(login: 'user-simple-import-fixed2')
+ assert(user1)
+ assert_equal('some note', user1.note)
+ assert_equal('user-simple-import-fixed1', user1.login)
+ assert_equal('firstname-simple-import-fixed1', user1.firstname)
+ assert_equal('lastname-simple-import-fixed1', user1.lastname)
+ assert_equal('user-simple-import-fixed1@example.com', user1.email)
+
+ assert(user2)
+ assert_equal('some note', user2.note)
+ assert_equal('user-simple-import-fixed2', user2.login)
+ assert_equal('firstname-simple-import-fixed2', user2.firstname)
+ assert_equal('lastname-simple-import-fixed2', user2.lastname)
+ assert_equal('user-simple-import-fixed2@example.com', user2.email)
+
+ user1.destroy!
+ user2.destroy!
+ end
+
+ test 'duplicate import' do
+
+ csv_string = "login;firstname;lastname;email\nuser-duplicate-import1;firstname-duplicate-import1;firstname-duplicate-import1;user-duplicate-import1@example.com\nuser-duplicate-import2;firstname-duplicate-import2;firstname-duplicate-import2;user-duplicate-import2@example.com\nuser-duplicate-import2;firstname-duplicate-import3;firstname-duplicate-import3;user-duplicate-import3@example.com"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(3, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert_nil(User.find_by(login: 'user-duplicate-import1'))
+ assert_nil(User.find_by(login: 'user-duplicate-import2'))
+ assert_nil(User.find_by(login: 'user-duplicate-import3'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(3, result[:records].count)
+ assert_equal('success', result[:result])
+
+ assert(User.find_by(login: 'user-duplicate-import1'))
+ assert(User.find_by(login: 'user-duplicate-import2'))
+ assert_nil(User.find_by(login: 'user-duplicate-import3'))
+
+ User.find_by(login: 'user-duplicate-import1').destroy!
+ User.find_by(login: 'user-duplicate-import2').destroy!
+ end
+
+ test 'invalid attributes' do
+
+ csv_string = "login;firstname2;lastname;email\nuser-invalid-import1;firstname-invalid-import1;firstname-invalid-import1;user-invalid-import1@example.com\nuser-invalid-import2;firstname-invalid-import2;firstname-invalid-import2;user-invalid-import2@example.com\n"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown attribute 'firstname2' for User.", result[:errors][0])
+ assert_equal("Line 2: unknown attribute 'firstname2' for User.", result[:errors][1])
+
+ assert_nil(User.find_by(login: 'user-invalid-import1'))
+ assert_nil(User.find_by(login: 'user-invalid-import2'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_equal("Line 1: unknown attribute 'firstname2' for User.", result[:errors][0])
+ assert_equal("Line 2: unknown attribute 'firstname2' for User.", result[:errors][1])
+
+ assert_nil(User.find_by(login: 'user-invalid-import1'))
+ assert_nil(User.find_by(login: 'user-invalid-import2'))
+ end
+
+ test 'reference import' do
+
+ csv_string = "login;firstname;lastname;email;organization\nuser-reference-import1;firstname-reference-import1;firstname-reference-import1;user-reference-import1@example.com;organization-reference-import1\nuser-reference-import2;firstname-reference-import2;firstname-reference-import2;user-reference-import2@example.com;organization-reference-import2\nuser-reference-import3;firstname-reference-import3;firstname-reference-import3;user-reference-import3@example.com;Zammad Foundation\n"
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+ assert_nil(User.find_by(login: 'user-reference-import1'))
+ assert_nil(User.find_by(login: 'user-reference-import2'))
+ assert_nil(User.find_by(login: 'user-reference-import3'))
+ assert_equal("Line 1: No lookup value found for 'organization': \"organization-reference-import1\"", result[:errors][0])
+ assert_equal("Line 2: No lookup value found for 'organization': \"organization-reference-import2\"", result[:errors][1])
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(2, result[:errors].count)
+ assert_equal('failed', result[:result])
+
+ assert_nil(User.find_by(login: 'user-reference-import1'))
+ assert_nil(User.find_by(login: 'user-reference-import2'))
+ assert(User.find_by(login: 'user-reference-import3'))
+ assert_equal("Line 1: No lookup value found for 'organization': \"organization-reference-import1\"", result[:errors][0])
+ assert_equal("Line 2: No lookup value found for 'organization': \"organization-reference-import2\"", result[:errors][1])
+
+ UserInfo.current_user_id = 1
+ orgaization1 = Organization.create_if_not_exists(name: 'organization-reference-import1')
+ orgaization2 = Organization.create_if_not_exists(name: 'organization-reference-import2')
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: true,
+ )
+ assert_equal(true, result[:try])
+ assert_equal(0, result[:errors].count)
+ assert_equal('success', result[:result])
+ assert_nil(User.find_by(login: 'user-reference-import1'))
+ assert_nil(User.find_by(login: 'user-reference-import2'))
+ assert(User.find_by(login: 'user-reference-import3'))
+
+ result = User.csv_import(
+ string: csv_string,
+ parse_params: {
+ col_sep: ';',
+ },
+ try: false,
+ )
+ assert_equal(false, result[:try])
+ assert_equal(0, result[:errors].count)
+ assert_equal('success', result[:result])
+
+ assert(User.find_by(login: 'user-reference-import1'))
+ assert(User.find_by(login: 'user-reference-import2'))
+ assert(User.find_by(login: 'user-reference-import3'))
+
+ User.find_by(login: 'user-reference-import1').destroy!
+ User.find_by(login: 'user-reference-import2').destroy!
+ User.find_by(login: 'user-reference-import3').destroy!
+
+ orgaization1.destroy!
+ orgaization2.destroy!
+ end
+
+end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index a6842b556..18414c71f 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -570,7 +570,6 @@ class UserTest < ActiveSupport::TestCase
test 'ensure roles' do
name = rand(999_999_999)
-
admin = User.create_or_update(
login: "admin-role#{name}@example.com",
firstname: 'Role',