diff --git a/app/assets/javascripts/app/controllers/import_zendesk.coffee b/app/assets/javascripts/app/controllers/import_zendesk.coffee index 1858adde1..c49004230 100644 --- a/app/assets/javascripts/app/controllers/import_zendesk.coffee +++ b/app/assets/javascripts/app/controllers/import_zendesk.coffee @@ -154,35 +154,35 @@ class Index extends App.ControllerContent processData: true success: (data, status, xhr) => - if data.result is 'import_done' - window.location.reload() - return - - if data.result is 'error' - @$('.js-error').removeClass('hide') - @$('.js-error').html(App.i18n.translateContent(data.message)) - else - @$('.js-error').addClass('hide') - - if data.message is 'not running' && @updateMigrationDisplayLoop > 16 + if _.isEmpty(data.result) && @updateMigrationDisplayLoop > 16 @$('.js-error').removeClass('hide') @$('.js-error').html(App.i18n.translateContent('Background process did not start or has not finished! Please contact your support.')) return - if data.result is 'in_progress' - for key, item of data.data - if item.done > item.total - item.done = item.total + if !_.isEmpty(data.result['error']) + @$('.js-error').removeClass('hide') + @$('.js-error').html(App.i18n.translateContent(data.result['error'])) + else + @$('.js-error').addClass('hide') - if key == 'Ticket' && item.total >= 1000 + if !_.isEmpty(data.finished_at) && _.isEmpty(data.result['error']) + window.location.reload() + return + + if !_.isEmpty(data.result) + for model, stats of data.result + if stats.sum > stats.total + stats.sum = stats.total + + if model == 'Ticket' && stats.total >= 1000 @ticketCountInfo.removeClass('hide') - element = @$('.js-' + key.toLowerCase() ) - element.find('.js-done').text(item.done) - element.find('.js-total').text(item.total) - element.find('progress').attr('max', item.total ) - element.find('progress').attr('value', item.done ) - if item.total <= item.done + element = @$('.js-' + model.toLowerCase() ) + element.find('.js-done').text(stats.sum) + element.find('.js-total').text(stats.total) + element.find('progress').attr('max', stats.total ) + element.find('progress').attr('value', stats.sum ) + if stats.total <= stats.sum element.addClass('is-done') else element.removeClass('is-done') diff --git a/app/assets/javascripts/app/views/import/zendesk.jst.eco b/app/assets/javascripts/app/views/import/zendesk.jst.eco index eeb7f95c9..e48d4d4a5 100644 --- a/app/assets/javascripts/app/views/import/zendesk.jst.eco +++ b/app/assets/javascripts/app/views/import/zendesk.jst.eco @@ -64,7 +64,7 @@
- + - + - + - +
-/- <%- @T('Groups') %> @@ -73,7 +73,7 @@ <%- @Icon('checkmark') %>
-/- <%- @T('Organizations') %> @@ -82,7 +82,7 @@ <%- @Icon('checkmark') %>
-/- <%- @T('Users') %> @@ -91,7 +91,7 @@ <%- @Icon('checkmark') %>
-/- <%- @T('Tickets') %> diff --git a/app/controllers/import_zendesk_controller.rb b/app/controllers/import_zendesk_controller.rb index 28bdbd609..c3ee3587c 100644 --- a/app/controllers/import_zendesk_controller.rb +++ b/app/controllers/import_zendesk_controller.rb @@ -74,7 +74,9 @@ class ImportZendeskController < ApplicationController Setting.set('import_zendesk_endpoint_username', params[:username]) Setting.set('import_zendesk_endpoint_key', params[:token]) - if !Import::Zendesk.connection_test + result = Sequencer.process('Import::Zendesk::ConnectionTest') + + if !result[:connected] Setting.set('import_zendesk_endpoint_username', nil) Setting.set('import_zendesk_endpoint_key', nil) @@ -96,8 +98,8 @@ class ImportZendeskController < ApplicationController Setting.set('import_mode', true) Setting.set('import_backend', 'zendesk') - # start migration - Import::Zendesk.delay.start_bg + job = ImportJob.create(name: 'Import::Zendesk') + job.delay.start render json: { result: 'ok', @@ -105,11 +107,13 @@ class ImportZendeskController < ApplicationController end def import_status - result = Import::Zendesk.status_bg - if result[:result] == 'import_done' + job = ImportJob.find_by(name: 'Import::Zendesk') + + if job.finished_at.present? Setting.reload end - render json: result + + model_show_render_item(job) end private diff --git a/lib/import/base_resource.rb b/lib/import/base_resource.rb deleted file mode 100644 index 04cb8efad..000000000 --- a/lib/import/base_resource.rb +++ /dev/null @@ -1,256 +0,0 @@ -module Import - class BaseResource - include Import::Helper - - attr_reader :resource, :errors - - def initialize(resource, *args) - @action = :unknown - handle_args(resource, *args) - initialize_associations_states - import(resource, *args) - end - - def import_class - raise NoMethodError, "#{self.class.name} has no implementation of the needed 'import_class' method" - end - - def source - self.class.source - end - - def remote_id(resource, *_args) - @remote_id ||= resource.delete(:id) - end - - def action - return :failed if errors.present? - return :skipped if @resource.blank? - return :unchanged if !attributes_changed? - @action - end - - def attributes_changed? - changed_attributes.present? || changed_associations.present? - end - - def changed_attributes - return if @resource.blank? - # dry run - return @resource.changes_to_save if @resource.has_changes_to_save? - # live run - @resource.previous_changes - end - - def changed_associations - changes = {} - tracked_associations.each do |association| - # skip if no new value will get assigned (no change is performed) - next if !@associations[:after].key?(association) - # skip if both values are equal - next if @associations[:before][association] == @associations[:after][association] - # skip if both values are blank - next if @associations[:before][association].blank? && @associations[:after][association].blank? - # store changes - changes[association] = [@associations[:before][association], @associations[:after][association]] - end - changes - end - - def self.source - import_class_namespace - end - - def self.import_class_namespace - @import_class_namespace ||= name.to_s.sub('Import::', '') - end - - private - - def initialize_associations_states - @associations = {} - %i[before after].each do |state| - @associations[state] ||= {} - end - end - - def import(resource, *args) - create_or_update(map(resource, *args), *args) - rescue => e - # Don't catch own thrown exceptions from above - raise if e.is_a?(NoMethodError) - handle_error(e) - end - - def create_or_update(resource, *args) - return if updated?(resource, *args) - create(resource, *args) - end - - def updated?(resource, *args) - @resource = lookup_existing(resource, *args) - return false if !@resource - - # lock the current resource for write access - @resource.with_lock do - - # delete since we have an update and - # the record is already created - resource.delete(:created_by_id) - - # store the current state of the associations - # from the resource hash because if we assign - # them to the instance some (e.g. has_many) - # will get stored even in the dry run :/ - store_associations(:after, resource) - - associations = tracked_associations - @resource.assign_attributes(resource.except(*associations)) - - # the return value here is kind of misleading - # and should not be trusted to indicate if a - # resource was actually updated. - # Use .action instead - return true if !attributes_changed? - - @action = :updated - - return true if @dry_run - @resource.assign_attributes(resource.slice(*associations)) - @resource.save! - true - end - end - - def lookup_existing(resource, *_args) - - synced_instance = ExternalSync.find_by( - source: source, - source_id: remote_id(resource), - object: import_class.name, - ) - return if !synced_instance - instance = import_class.find_by(id: synced_instance.o_id) - - store_associations(:before, instance) - - instance - end - - def store_associations(state, source) - @associations[state] = associations_state(source) - end - - def associations_state(source) - state = {} - tracked_associations.each do |association| - # we have to support instances and (resource) hashes - # here since in case of an update we only have the - # hash as a source but on create we have an instance - if source.is_a?(Hash) - # ignore if there is no key for the association - # of the Hash (update) - # otherwise wrong changes may get detected - next if !source.key?(association) - state[association] = source[association] - else - state[association] = source.send(association) - end - - # sort arrays to avoid wrong change detection - next if !state[association].respond_to?(:sort!) - state[association].sort! - end - state - end - - def tracked_associations - # loop over all reflections - import_class.reflect_on_all_associations.collect do |reflection| - # refection name is something like groups or organization (singular/plural) - reflection_name = reflection.name.to_s - # key is something like group_id or organization_id (singular) - key = reflection.klass.name.foreign_key - - # add trailing 's' to get pluralized key - if reflection_name.singularize != reflection_name - key = "#{key}s" - end - - key.to_sym - end - end - - def create(resource, *_args) - @resource = import_class.new(resource) - store_associations(:after, @resource) - @action = :created - return if @dry_run - @resource.save! - external_sync_create( - local: @resource, - remote: resource, - ) - end - - def external_sync_create(local:, remote:) - ExternalSync.create( - source: source, - source_id: remote_id(remote), - object: import_class.name, - o_id: local.id - ) - end - - def defaults(_resource, *_args) - { - created_by_id: 1, - updated_by_id: 1, - } - end - - def map(resource, *args) - mapped = from_mapping(resource, *args) - attributes = defaults(resource, *args).merge(mapped) - attributes.symbolize_keys - end - - def from_mapping(resource, *args) - mapping = mapping(*args) - return resource if !mapping - - ExternalSync.map( - mapping: mapping, - source: resource - ) - end - - def mapping(*args) - Setting.get(mapping_config(*args)) - end - - def mapping_config(*_args) - self.class.import_class_namespace.gsub('::', '_').underscore + '_mapping' - end - - def handle_args(_resource, *args) - return if !args - return if !args.is_a?(Array) - return if args.blank? - - last_arg = args.last - return if !last_arg.is_a?(Hash) - handle_modifiers(last_arg) - end - - def handle_modifiers(modifiers) - @dry_run = modifiers.fetch(:dry_run, false) - end - - def handle_error(e) - @errors ||= [] - @errors.push(e) - Rails.logger.error e - end - end -end diff --git a/lib/import/zendesk.rb b/lib/import/zendesk.rb index 110124103..ff143d281 100644 --- a/lib/import/zendesk.rb +++ b/lib/import/zendesk.rb @@ -1,68 +1,15 @@ -require 'base64' -require 'zendesk_api' +# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/ module Import -end -module Import::Zendesk - extend Import::Helper - extend Import::Zendesk::Async - extend Import::Zendesk::ImportStats + class Zendesk < Import::Base + include Import::Mixin::Sequence - # rubocop:disable Style/ModuleFunction - extend self + def start + process + end - def start - log 'Start import...' - - checks - - Import::Zendesk::GroupFactory.import(client.groups) - - Import::Zendesk::OrganizationFieldFactory.import(client.organization_fields) - Import::Zendesk::OrganizationFactory.import(client.organizations) - - Import::Zendesk::UserFieldFactory.import(client.user_fields) - Import::Zendesk::UserFactory.import(client.users) - - Import::Zendesk::TicketFieldFactory.import(client.ticket_fields) - Import::Zendesk::TicketFactory.import(all_tickets) - - # TODO - Setting.set( 'system_init_done', true ) - Setting.set( 'import_mode', false ) - - true - end - - def connection_test - Import::Zendesk::Requester.connection_test - end - - private - - # this special ticket logic is needed since Zendesk archives tickets - # after 120 days and doesn't return them via the client.tickets - # endpoint as described here: - # https://github.com/zammad/zammad/issues/558#issuecomment-267951351 - # the proper way is to use the 'incremental' endpoint which is not available - # via the ruby gem yet but a pull request is pending: - # https://github.com/zendesk/zendesk_api_client_rb/pull/287 - # the following workaround is needed to use this functionality - def all_tickets - ZendeskAPI::Collection.new( - client, - ZendeskAPI::Ticket, - path: 'incremental/tickets?start_time=1' - ) - end - - def client - Import::Zendesk::Requester.client - end - - def checks - check_import_mode - check_system_init_done - connection_test + def sequence_name + 'Import::Zendesk::Full' + end end end diff --git a/lib/import/zendesk/async.rb b/lib/import/zendesk/async.rb deleted file mode 100644 index 7ae735bc8..000000000 --- a/lib/import/zendesk/async.rb +++ /dev/null @@ -1,64 +0,0 @@ -module Import - module Zendesk - module Async - # rubocop:disable Style/ModuleFunction - extend self - - def start_bg - Setting.reload - - Import::Zendesk.connection_test - - # get statistic before starting import - statistic - - # start thread to observe current state - status_update_thread = Thread.new do - loop do - result = { - data: current_state, - result: 'in_progress', - } - Cache.write('import:state', result, expires_in: 10.minutes) - sleep 8 - end - end - sleep 2 - - # start import data - begin - Import::Zendesk.start - rescue => e - status_update_thread.exit - status_update_thread.join - Rails.logger.error e - result = { - message: e.message, - result: 'error', - } - Cache.write('import:state', result, expires_in: 10.hours) - return false - end - sleep 16 # wait until new finished import state is on client - status_update_thread.exit - status_update_thread.join - - result = { - result: 'import_done', - } - Cache.write('import:state', result, expires_in: 10.hours) - - Setting.set('system_init_done', true) - Setting.set('import_mode', false) - end - - def status_bg - state = Cache.get('import:state') - return state if state - { - message: 'not running', - } - end - end - end -end diff --git a/lib/import/zendesk/base_factory.rb b/lib/import/zendesk/base_factory.rb deleted file mode 100644 index 3e2753b7e..000000000 --- a/lib/import/zendesk/base_factory.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Import - module Zendesk - module BaseFactory - include Import::Factory - - # rubocop:disable Style/ModuleFunction - extend self - - private - - def import_loop(records, *_args, &import_block) - records.all!(&import_block) - end - end - end -end diff --git a/lib/import/zendesk/group.rb b/lib/import/zendesk/group.rb deleted file mode 100644 index 0c0c14566..000000000 --- a/lib/import/zendesk/group.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Import - module Zendesk - class Group - include Import::Helper - - attr_reader :zendesk_id, :id - - def initialize(group) - local_group = ::Group.create_if_not_exists( - name: group.name, - active: !group.deleted, - updated_by_id: 1, - created_by_id: 1 - ) - - @zendesk_id = group.id - @id = local_group.id - end - end - end -end diff --git a/lib/import/zendesk/group_factory.rb b/lib/import/zendesk/group_factory.rb deleted file mode 100644 index 3386da928..000000000 --- a/lib/import/zendesk/group_factory.rb +++ /dev/null @@ -1,8 +0,0 @@ -module Import - module Zendesk - module GroupFactory - extend Import::Zendesk::BaseFactory - extend Import::Zendesk::LocalIDMapperHook - end - end -end diff --git a/lib/import/zendesk/helper.rb b/lib/import/zendesk/helper.rb deleted file mode 100644 index 34c606b71..000000000 --- a/lib/import/zendesk/helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Import - module Zendesk - module Helper - # rubocop:disable Style/ModuleFunction - extend self - - private - - def get_fields(zendesk_fields) - return {} if !zendesk_fields - fields = {} - zendesk_fields.each do |key, value| - fields[key] = value - end - fields - end - end - end -end diff --git a/lib/import/zendesk/import_stats.rb b/lib/import/zendesk/import_stats.rb deleted file mode 100644 index 28773ba5c..000000000 --- a/lib/import/zendesk/import_stats.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Import - module Zendesk - module ImportStats - # rubocop:disable Style/ModuleFunction - extend self - - def current_state - - data = statistic - - { - Group: { - done: ::Group.count, - total: data['Groups'] || 0, - }, - Organization: { - done: ::Organization.count, - total: data['Organizations'] || 0, - }, - User: { - done: ::User.count, - total: data['Users'] || 0, - }, - Ticket: { - done: ::Ticket.count, - total: data['Tickets'] || 0, - }, - } - end - - def statistic - - # check cache - cache = Cache.get('import_zendesk_stats') - return cache if cache - - # retrive statistic - result = { - 'Tickets' => 0, - 'TicketFields' => 0, - 'UserFields' => 0, - 'OrganizationFields' => 0, - 'Groups' => 0, - 'Organizations' => 0, - 'Users' => 0, - 'GroupMemberships' => 0, - 'Macros' => 0, - 'Views' => 0, - 'Automations' => 0, - } - - result.each_key do |object| - result[ object ] = statistic_count(object) - end - - Cache.write('import_zendesk_stats', result) - result - end - - private - - def statistic_count(object) - statistic_count_data(object).count! - end - - def statistic_count_data(object) - return all_tickets if object == 'Tickets' - Import::Zendesk::Requester.client.send( object.underscore.to_sym ) - end - end - end -end diff --git a/lib/import/zendesk/local_id_mapper_hook.rb b/lib/import/zendesk/local_id_mapper_hook.rb deleted file mode 100644 index b300caa9a..000000000 --- a/lib/import/zendesk/local_id_mapper_hook.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Import - module Zendesk - module LocalIDMapperHook - - # rubocop:disable Style/ModuleFunction - extend self - - def local_id(zendesk_id) - init_mapping - @zendesk_mapping[ zendesk_id ] - end - - def post_import_hook(_record, backend_instance) - init_mapping - @zendesk_mapping[ backend_instance.zendesk_id ] = backend_instance.id - end - - private - - def init_mapping - @zendesk_mapping ||= {} - end - end - end -end diff --git a/lib/import/zendesk/object_attribute.rb b/lib/import/zendesk/object_attribute.rb deleted file mode 100644 index 440f2b220..000000000 --- a/lib/import/zendesk/object_attribute.rb +++ /dev/null @@ -1,74 +0,0 @@ -module Import - module Zendesk - class ObjectAttribute - - def initialize(object, name, attribute) - - initialize_data_option(attribute) - init_callback(attribute) - - add(object, name, attribute) - end - - private - - def init_callback(_attribute); end - - def add(object, name, attribute) - ObjectManager::Attribute.add( attribute_config(object, name, attribute) ) - ObjectManager::Attribute.migration_execute(false) - rescue => e - # rubocop:disable Style/SpecialGlobalVars - raise $!, "Problem with ObjectManager Attribute '#{name}': #{$!}", $!.backtrace - end - - def attribute_config(object, name, attribute) - { - object: object, - name: name, - display: attribute.title, - data_type: data_type(attribute), - data_option: @data_option, - editable: !attribute.removable, - active: attribute.active, - screens: screens(attribute), - position: attribute.position, - created_by_id: 1, - updated_by_id: 1, - } - end - - def screens(attribute) - config = { - view: { - '-all-' => { - shown: true, - }, - } - } - - return config if !attribute.visible_in_portal && attribute.required_in_portal - - { - edit: { - Customer: { - shown: attribute.visible_in_portal, - null: !attribute.required_in_portal, - }, - }.merge(config) - } - end - - def initialize_data_option(attribute) - @data_option = { - null: !attribute.required, - note: attribute.description, - } - end - - def data_type(attribute) - attribute.type - end - end - end -end diff --git a/lib/import/zendesk/object_attribute/base.rb b/lib/import/zendesk/object_attribute/base.rb new file mode 100644 index 000000000..a444a1c3f --- /dev/null +++ b/lib/import/zendesk/object_attribute/base.rb @@ -0,0 +1,76 @@ +module Import + class Zendesk + module ObjectAttribute + class Base + + def initialize(object, name, attribute) + + initialize_data_option(attribute) + init_callback(attribute) + + add(object, name, attribute) + end + + private + + def init_callback(_attribute); end + + def add(object, name, attribute) + ObjectManager::Attribute.add( attribute_config(object, name, attribute) ) + ObjectManager::Attribute.migration_execute(false) + rescue => e + # rubocop:disable Style/SpecialGlobalVars + raise $!, "Problem with ObjectManager Attribute '#{name}': #{$!}", $!.backtrace + end + + def attribute_config(object, name, attribute) + { + object: object, + name: name, + display: attribute.title, + data_type: data_type(attribute), + data_option: @data_option, + editable: !attribute.removable, + active: attribute.active, + screens: screens(attribute), + position: attribute.position, + created_by_id: 1, + updated_by_id: 1, + } + end + + def screens(attribute) + config = { + view: { + '-all-' => { + shown: true, + }, + } + } + + return config if !attribute.visible_in_portal && attribute.required_in_portal + + { + edit: { + Customer: { + shown: attribute.visible_in_portal, + null: !attribute.required_in_portal, + }, + }.merge(config) + } + end + + def initialize_data_option(attribute) + @data_option = { + null: !attribute.required, + note: attribute.description, + } + end + + def data_type(attribute) + attribute.type + end + end + end + end +end diff --git a/lib/import/zendesk/object_attribute/checkbox.rb b/lib/import/zendesk/object_attribute/checkbox.rb index 143b629e6..310712126 100644 --- a/lib/import/zendesk/object_attribute/checkbox.rb +++ b/lib/import/zendesk/object_attribute/checkbox.rb @@ -1,7 +1,13 @@ +# this require is required (hehe) because of Rails autoloading +# which causes strange behavior not inheriting correctly +# from Import::OTRS::DynamicField +require 'import/zendesk/object_attribute/base' + module Import - module Zendesk - class ObjectAttribute - class Checkbox < Import::Zendesk::ObjectAttribute + class Zendesk + module ObjectAttribute + class Checkbox < Import::Zendesk::ObjectAttribute::Base + def init_callback(_object_attribte) @data_option.merge!( default: false, diff --git a/lib/import/zendesk/object_attribute/date.rb b/lib/import/zendesk/object_attribute/date.rb index ed7ba4165..7e7c57df7 100644 --- a/lib/import/zendesk/object_attribute/date.rb +++ b/lib/import/zendesk/object_attribute/date.rb @@ -1,12 +1,12 @@ # this require is required (hehe) because of Rails autoloading # which causes strange behavior not inheriting correctly # from Import::OTRS::DynamicField -require 'import/zendesk/object_attribute' +require 'import/zendesk/object_attribute/base' module Import - module Zendesk - class ObjectAttribute - class Date < Import::Zendesk::ObjectAttribute + class Zendesk + module ObjectAttribute + class Date < Import::Zendesk::ObjectAttribute::Base def init_callback(_object_attribte) @data_option.merge!( future: true, diff --git a/lib/import/zendesk/object_attribute/decimal.rb b/lib/import/zendesk/object_attribute/decimal.rb index 6067a7540..24e9f7a4b 100644 --- a/lib/import/zendesk/object_attribute/decimal.rb +++ b/lib/import/zendesk/object_attribute/decimal.rb @@ -1,6 +1,6 @@ module Import - module Zendesk - class ObjectAttribute + class Zendesk + module ObjectAttribute class Decimal < Import::Zendesk::ObjectAttribute::Text end end diff --git a/lib/import/zendesk/object_attribute/dropdown.rb b/lib/import/zendesk/object_attribute/dropdown.rb index 871e87df1..20ce3990a 100644 --- a/lib/import/zendesk/object_attribute/dropdown.rb +++ b/lib/import/zendesk/object_attribute/dropdown.rb @@ -1,6 +1,6 @@ module Import - module Zendesk - class ObjectAttribute + class Zendesk + module ObjectAttribute class Dropdown < Import::Zendesk::ObjectAttribute::Select end end diff --git a/lib/import/zendesk/object_attribute/integer.rb b/lib/import/zendesk/object_attribute/integer.rb index d16e94233..eb8a2ab22 100644 --- a/lib/import/zendesk/object_attribute/integer.rb +++ b/lib/import/zendesk/object_attribute/integer.rb @@ -1,12 +1,13 @@ # this require is required (hehe) because of Rails autoloading # which causes strange behavior not inheriting correctly # from Import::OTRS::DynamicField -require 'import/zendesk/object_attribute' +require 'import/zendesk/object_attribute/base' module Import - module Zendesk - class ObjectAttribute - class Integer < Import::Zendesk::ObjectAttribute + class Zendesk + module ObjectAttribute + class Integer < Import::Zendesk::ObjectAttribute::Base + def init_callback(_object_attribte) @data_option.merge!( min: 0, diff --git a/lib/import/zendesk/object_attribute/regexp.rb b/lib/import/zendesk/object_attribute/regexp.rb index b557dab66..b56093acc 100644 --- a/lib/import/zendesk/object_attribute/regexp.rb +++ b/lib/import/zendesk/object_attribute/regexp.rb @@ -1,12 +1,13 @@ # this require is required (hehe) because of Rails autoloading # which causes strange behavior not inheriting correctly # from Import::OTRS::DynamicField -require 'import/zendesk/object_attribute' +require 'import/zendesk/object_attribute/base' module Import - module Zendesk - class ObjectAttribute - class Regexp < Import::Zendesk::ObjectAttribute + class Zendesk + module ObjectAttribute + class Regexp < Import::Zendesk::ObjectAttribute::Base + def init_callback(object_attribte) @data_option.merge!( type: 'text', diff --git a/lib/import/zendesk/object_attribute/select.rb b/lib/import/zendesk/object_attribute/select.rb index c041adec4..43ca8633e 100644 --- a/lib/import/zendesk/object_attribute/select.rb +++ b/lib/import/zendesk/object_attribute/select.rb @@ -1,7 +1,13 @@ +# this require is required (hehe) because of Rails autoloading +# which causes strange behavior not inheriting correctly +# from Import::OTRS::DynamicField +require 'import/zendesk/object_attribute/base' + module Import - module Zendesk - class ObjectAttribute - class Select < Import::Zendesk::ObjectAttribute + class Zendesk + module ObjectAttribute + class Select < Import::Zendesk::ObjectAttribute::Base + def init_callback(object_attribte) @data_option.merge!( default: '', diff --git a/lib/import/zendesk/object_attribute/tagger.rb b/lib/import/zendesk/object_attribute/tagger.rb index c8481b586..7db751300 100644 --- a/lib/import/zendesk/object_attribute/tagger.rb +++ b/lib/import/zendesk/object_attribute/tagger.rb @@ -1,6 +1,6 @@ module Import - module Zendesk - class ObjectAttribute + class Zendesk + module ObjectAttribute class Tagger < Import::Zendesk::ObjectAttribute::Select end end diff --git a/lib/import/zendesk/object_attribute/text.rb b/lib/import/zendesk/object_attribute/text.rb index 3d68dfcbe..6676173e4 100644 --- a/lib/import/zendesk/object_attribute/text.rb +++ b/lib/import/zendesk/object_attribute/text.rb @@ -1,7 +1,13 @@ +# this require is required (hehe) because of Rails autoloading +# which causes strange behavior not inheriting correctly +# from Import::OTRS::DynamicField +require 'import/zendesk/object_attribute/base' + module Import - module Zendesk - class ObjectAttribute - class Text < Import::Zendesk::ObjectAttribute + class Zendesk + module ObjectAttribute + class Text < Import::Zendesk::ObjectAttribute::Base + def init_callback(_object_attribte) @data_option.merge!( type: 'text', diff --git a/lib/import/zendesk/object_attribute/textarea.rb b/lib/import/zendesk/object_attribute/textarea.rb index f2e6b9013..9562d3d0a 100644 --- a/lib/import/zendesk/object_attribute/textarea.rb +++ b/lib/import/zendesk/object_attribute/textarea.rb @@ -1,7 +1,13 @@ +# this require is required (hehe) because of Rails autoloading +# which causes strange behavior not inheriting correctly +# from Import::OTRS::DynamicField +require 'import/zendesk/object_attribute/base' + module Import - module Zendesk - class ObjectAttribute - class Textarea < Import::Zendesk::ObjectAttribute + class Zendesk + module ObjectAttribute + class Textarea < Import::Zendesk::ObjectAttribute::Base + def init_callback(_object_attribte) @data_option.merge!( type: 'textarea', diff --git a/lib/import/zendesk/object_field.rb b/lib/import/zendesk/object_field.rb deleted file mode 100644 index 814a8e913..000000000 --- a/lib/import/zendesk/object_field.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Import - module Zendesk - class ObjectField - - attr_reader :zendesk_id, :id - - def initialize(object_field) - - import(object_field) - - @zendesk_id = object_field.id - @id = local_name(object_field) - end - - private - - def local_name(object_field) - @local_name ||= remote_name(object_field).gsub(%r{[\s\/]}, '_').underscore.gsub(/_{2,}/, '_').gsub(/_id(s?)$/, '_no\1') - end - - def remote_name(object_field) - object_field['key'] # TODO: y?! - end - - def import(object_field) - backend_class(object_field).new(object_name, local_name(object_field), object_field) - end - - def backend_class(object_field) - "Import::Zendesk::ObjectAttribute::#{object_field.type.capitalize}".constantize - end - - def object_name - self.class.name.to_s.sub(/Import::Zendesk::/, '').sub(/Field/, '') - end - end - end -end diff --git a/lib/import/zendesk/organization.rb b/lib/import/zendesk/organization.rb deleted file mode 100644 index 961035c29..000000000 --- a/lib/import/zendesk/organization.rb +++ /dev/null @@ -1,34 +0,0 @@ -# https://developer.zendesk.com/rest_api/docs/core/organizations -module Import - module Zendesk - class Organization - include Import::Zendesk::Helper - - attr_reader :zendesk_id, :id - - def initialize(organization) - local_organization = ::Organization.create_if_not_exists(local_organization_fields(organization)) - @zendesk_id = organization.id - @id = local_organization.id - end - - private - - def local_organization_fields(organization) - { - name: organization.name, - note: organization.note, - shared: organization.shared_tickets, - # shared: organization.shared_comments, # TODO, not yet implemented - # }.merge(organization.organization_fields) # TODO - updated_by_id: 1, - created_by_id: 1 - }.merge(custom_fields(organization)) - end - - def custom_fields(organization) - get_fields(organization.organization_fields) - end - end - end -end diff --git a/lib/import/zendesk/organization_factory.rb b/lib/import/zendesk/organization_factory.rb deleted file mode 100644 index 5f1af7584..000000000 --- a/lib/import/zendesk/organization_factory.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Import - module Zendesk - module OrganizationFactory - # we need to loop over each instead of all! - # so we can use the default import factory here - extend Import::Factory - extend Import::Zendesk::LocalIDMapperHook - end - end -end diff --git a/lib/import/zendesk/organization_field.rb b/lib/import/zendesk/organization_field.rb deleted file mode 100644 index f29fb53ef..000000000 --- a/lib/import/zendesk/organization_field.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Import - module Zendesk - class OrganizationField < Import::Zendesk::ObjectField - end - end -end diff --git a/lib/import/zendesk/organization_field_factory.rb b/lib/import/zendesk/organization_field_factory.rb deleted file mode 100644 index cf235422b..000000000 --- a/lib/import/zendesk/organization_field_factory.rb +++ /dev/null @@ -1,8 +0,0 @@ -module Import - module Zendesk - module OrganizationFieldFactory - extend Import::Zendesk::BaseFactory - extend Import::Zendesk::LocalIDMapperHook - end - end -end diff --git a/lib/import/zendesk/priority.rb b/lib/import/zendesk/priority.rb deleted file mode 100644 index 25b7e9c44..000000000 --- a/lib/import/zendesk/priority.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Import - module Zendesk - class Priority - - MAPPING = { - 'low' => '1 low', - nil => '2 normal', - 'normal' => '2 normal', - 'high' => '3 high', - 'urgent' => '3 high', - }.freeze - - class << self - - def lookup(ticket) - remote_priority = ticket.priority - @mapping ||= {} - if @mapping[ remote_priority ] - return @mapping[ remote_priority ] - end - @mapping[ remote_priority ] = ::Ticket::Priority.lookup( name: map(remote_priority) ) - end - - private - - def map(priority) - MAPPING.fetch(priority, MAPPING[nil]) - end - end - end - end -end diff --git a/lib/import/zendesk/requester.rb b/lib/import/zendesk/requester.rb deleted file mode 100644 index 53b717e4f..000000000 --- a/lib/import/zendesk/requester.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Import - module Zendesk - module Requester - - # rubocop:disable Style/ModuleFunction - extend self - - def connection_test - # make sure to reinitialize client - # to react to config changes - initialize_client - return true if client.users.first - false - end - - def client - return @client if @client - initialize_client - @client - end - - private - - def initialize_client - @client = ZendeskAPI::Client.new do |config| - config.url = Setting.get('import_zendesk_endpoint') - - # Basic / Token Authentication - config.username = Setting.get('import_zendesk_endpoint_username') - config.token = Setting.get('import_zendesk_endpoint_key') - - # when hitting the rate limit, sleep automatically, - # then retry the request. - config.retry = true - - # disable cache to avoid unneeded memory consumption - # since we are using each object only once - # Inspired by: https://medium.com/swiftype-engineering/using-jmat-to-find-analyze-memory-in-jruby-1c4196c1ec72 - config.cache = false - end - end - end - end -end diff --git a/lib/import/zendesk/state.rb b/lib/import/zendesk/state.rb deleted file mode 100644 index 30dfd4895..000000000 --- a/lib/import/zendesk/state.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Import - module Zendesk - class State - - MAPPING = { - 'pending' => 'pending reminder', - 'solved' => 'closed', - 'deleted' => 'removed', - }.freeze - - class << self - - def lookup(ticket) - remote_state = ticket.status - @mapping ||= {} - if @mapping[ remote_state ] - return @mapping[ remote_state ] - end - @mapping[ remote_state ] = ::Ticket::State.lookup( name: map( remote_state ) ) - end - - private - - def map(state) - MAPPING.fetch(state, state) - end - end - end - end -end diff --git a/lib/import/zendesk/ticket.rb b/lib/import/zendesk/ticket.rb deleted file mode 100644 index 7a18c0079..000000000 --- a/lib/import/zendesk/ticket.rb +++ /dev/null @@ -1,74 +0,0 @@ -# https://developer.zendesk.com/rest_api/docs/core/tickets -# https://developer.zendesk.com/rest_api/docs/core/ticket_comments#ticket-comments -# https://developer.zendesk.com/rest_api/docs/core/ticket_audits#the-via-object -# https://developer.zendesk.com/rest_api/docs/help_center/article_attachments -# https://developer.zendesk.com/rest_api/docs/core/ticket_audits # v2 -module Import - module Zendesk - class Ticket - include Import::Helper - - def initialize(ticket) - create_or_update(ticket) - Import::Zendesk::Ticket::TagFactory.import(ticket.tags, @local_ticket, ticket) - Import::Zendesk::Ticket::CommentFactory.import(ticket.comments, @local_ticket, ticket) - end - - private - - def create_or_update(ticket) - mapped_ticket = local_ticket_fields(ticket) - return if updated?(mapped_ticket) - create(mapped_ticket) - end - - def updated?(ticket) - @local_ticket = ::Ticket.find_by(id: ticket[:id]) - return false if !@local_ticket - @local_ticket.update!(ticket) - true - end - - def create(ticket) - @local_ticket = ::Ticket.create(ticket) - reset_primary_key_sequence('tickets') - end - - def local_ticket_fields(ticket) - local_user_id = Import::Zendesk::UserFactory.local_id( ticket.requester_id ) || 1 - - { - id: ticket.id, - title: ticket.subject || ticket.description || '-', - owner_id: Import::Zendesk::UserFactory.local_id( ticket.assignee ) || 1, - note: ticket.description, - group_id: Import::Zendesk::GroupFactory.local_id( ticket.group_id ) || 1, - customer_id: local_user_id, - organization_id: Import::Zendesk::OrganizationFactory.local_id( ticket.organization_id ), - priority: Import::Zendesk::Priority.lookup(ticket), - state: Import::Zendesk::State.lookup(ticket), - pending_time: ticket.due_at, - updated_at: ticket.updated_at, - created_at: ticket.created_at, - updated_by_id: local_user_id, - created_by_id: local_user_id, - create_article_sender_id: Import::Zendesk::Ticket::Comment::Sender.local_id(local_user_id), - create_article_type_id: Import::Zendesk::Ticket::Comment::Type.local_id(ticket), - }.merge(custom_fields(ticket)) - end - - def custom_fields(ticket) - custom_fields = ticket.custom_fields - fields = {} - return fields if !custom_fields - custom_fields.each do |custom_field| - field_name = Import::Zendesk::TicketFieldFactory.local_id(custom_field['id']) - field_value = custom_field['value'] - next if field_value.nil? - fields[ field_name.to_sym ] = field_value - end - fields - end - end - end -end diff --git a/lib/import/zendesk/ticket/comment.rb b/lib/import/zendesk/ticket/comment.rb deleted file mode 100644 index d03f3b657..000000000 --- a/lib/import/zendesk/ticket/comment.rb +++ /dev/null @@ -1,71 +0,0 @@ -module Import - module Zendesk - class Ticket - class Comment - - def initialize(comment, local_ticket, _zendesk_ticket) - create_or_update(comment, local_ticket) - import_attachments(comment) - end - - private - - def create_or_update(comment, local_ticket) - mapped_article = local_article_fields(comment, local_ticket) - return if updated?(mapped_article) - create(mapped_article) - end - - def updated?(article) - @local_article = ::Ticket::Article.find_by(message_id: article[:message_id]) - return false if !@local_article - @local_article.update!(article) - true - end - - def create(article) - @local_article = ::Ticket::Article.create(article) - end - - def local_article_fields(comment, local_ticket) - - local_user_id = Import::Zendesk::UserFactory.local_id( comment.author_id ) || 1 - - { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: Import::Zendesk::Ticket::Comment::Sender.local_id( local_user_id ), - type_id: Import::Zendesk::Ticket::Comment::Type.local_id(comment), - }.merge(from_to(comment)) - end - - def from_to(comment) - if comment.via.channel == 'email' - { - from: comment.via.source.from.address, - to: comment.via.source.to.address # Notice comment.via.from.original_recipients = [\"another@gmail.com\", \"support@example.zendesk.com\"] - } - elsif comment.via.channel == 'facebook' - { - from: comment.via.source.from.facebook_id, - to: comment.via.source.to.facebook_id - } - else - {} - end - end - - def import_attachments(comment) - attachments = comment.attachments - return if attachments.blank? - Import::Zendesk::Ticket::Comment::AttachmentFactory.import(attachments, @local_article) - end - end - end - end -end diff --git a/lib/import/zendesk/ticket/comment/attachment.rb b/lib/import/zendesk/ticket/comment/attachment.rb deleted file mode 100644 index 15728baf4..000000000 --- a/lib/import/zendesk/ticket/comment/attachment.rb +++ /dev/null @@ -1,46 +0,0 @@ -module Import - module Zendesk - class Ticket - class Comment - class Attachment - include Import::Helper - - def initialize(attachment, local_article) - - response = request(attachment) - return if !response - - ::Store.add( - object: 'Ticket::Article', - o_id: local_article.id, - data: response.body, - filename: attachment.file_name, - preferences: { - 'Content-Type' => attachment.content_type - }, - created_by_id: 1 - ) - rescue => e - log e.message - end - - private - - def request(attachment) - response = UserAgent.get( - attachment.content_url, - {}, - { - open_timeout: 10, - read_timeout: 60, - }, - ) - return response if response.success? - log response.error - nil - end - end - end - end - end -end diff --git a/lib/import/zendesk/ticket/comment/attachment_factory.rb b/lib/import/zendesk/ticket/comment/attachment_factory.rb deleted file mode 100644 index 167ff8192..000000000 --- a/lib/import/zendesk/ticket/comment/attachment_factory.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Import - module Zendesk - class Ticket - class Comment - module AttachmentFactory - # we need to loop over each instead of all! - # so we can use the default import factory here - extend Import::Factory - - # rubocop:disable Style/ModuleFunction - extend self - - private - - # special handling which only starts import if needed - # Attention: skip? method can't be used since it (currently) - # only checks for single records - not all - def import_loop(records, *args, &import_block) - local_article = args[0] - local_attachments = local_article.attachments - - return if local_attachments.count == records.count - # get a common ground - local_attachments.each(&:delete) - return if records.blank? - - records.each(&import_block) - end - - def create_instance(record, *args) - local_article = args[0] - backend_class(record).new(record, local_article) - end - end - end - end - end -end diff --git a/lib/import/zendesk/ticket/comment/sender.rb b/lib/import/zendesk/ticket/comment/sender.rb deleted file mode 100644 index f8d3ab028..000000000 --- a/lib/import/zendesk/ticket/comment/sender.rb +++ /dev/null @@ -1,49 +0,0 @@ -module Import - module Zendesk - class Ticket - class Comment - module Sender - - # rubocop:disable Style/ModuleFunction - extend self - - def local_id(user_id) - author = author_lookup(user_id) - sender_id(author) - end - - private - - def author_lookup(user_id) - ::User.find( user_id ) - end - - def sender_id(author) - if author.role?('Customer') - article_sender_customer - elsif author.role?('Agent') - article_sender_agent - else - article_sender_system - end - end - - def article_sender_customer - return @article_sender_customer if @article_sender_customer - @article_sender_customer = ::Ticket::Article::Sender.lookup(name: 'Customer').id - end - - def article_sender_agent - return @article_sender_agent if @article_sender_agent - @article_sender_agent = ::Ticket::Article::Sender.lookup(name: 'Agent').id - end - - def article_sender_system - return @article_sender_system if @article_sender_system - @article_sender_system = ::Ticket::Article::Sender.lookup(name: 'System').id - end - end - end - end - end -end diff --git a/lib/import/zendesk/ticket/comment/type.rb b/lib/import/zendesk/ticket/comment/type.rb deleted file mode 100644 index 8dad31905..000000000 --- a/lib/import/zendesk/ticket/comment/type.rb +++ /dev/null @@ -1,60 +0,0 @@ -module Import - module Zendesk - class Ticket - class Comment - module Type - - # rubocop:disable Style/ModuleFunction - extend self - - def local_id(object) - case object.via.channel - when 'web' - article_type_id[:web] - when 'email' - article_type_id[:email] - when 'sample_ticket' - article_type_id[:note] - when 'twitter' - if object.via.source.rel == 'mention' - article_type_id[:twitter_status] - else - article_type_id[:twitter_direct_message] - end - when 'facebook' - if object.via.source.rel == 'post' - article_type_id[:facebook_feed_post] - else - article_type_id[:facebook_feed_comment] - end - # fallback for other not (yet) supported article types - # See: - # https://support.zendesk.com/hc/en-us/articles/203661746-Zendesk-Glossary#topic_zie_aqe_tf - # https://support.zendesk.com/hc/en-us/articles/203661596-About-Zendesk-Support-channels - else - article_type_id[:web] - end - end - - private - - def article_type_id - return @article_type_id if @article_type_id - - article_types = ['web', 'note', 'email', 'twitter status', - 'twitter direct-message', 'facebook feed post', - 'facebook feed comment'] - @article_type_id = {} - article_types.each do |article_type| - - article_type_key = article_type.gsub(/\s|\-/, '_').to_sym - - @article_type_id[article_type_key] = ::Ticket::Article::Type.lookup(name: article_type).id - end - @article_type_id - end - end - end - end - end -end diff --git a/lib/import/zendesk/ticket/comment_factory.rb b/lib/import/zendesk/ticket/comment_factory.rb deleted file mode 100644 index 99f96429d..000000000 --- a/lib/import/zendesk/ticket/comment_factory.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Import - module Zendesk - class Ticket - module CommentFactory - extend Import::Zendesk::Ticket::SubObjectFactory - end - end - end -end diff --git a/lib/import/zendesk/ticket/sub_object_factory.rb b/lib/import/zendesk/ticket/sub_object_factory.rb deleted file mode 100644 index aa67e0861..000000000 --- a/lib/import/zendesk/ticket/sub_object_factory.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Import - module Zendesk - class Ticket - module SubObjectFactory - # we need to loop over each instead of all! - # so we can use the default import factory here - include Import::Factory - - private - - def create_instance(record, *args) - - local_ticket = args[0] - zendesk_ticket = args[1] - - backend_class(record).new(record, local_ticket, zendesk_ticket) - end - end - end - end -end diff --git a/lib/import/zendesk/ticket/tag.rb b/lib/import/zendesk/ticket/tag.rb deleted file mode 100644 index f4a07a079..000000000 --- a/lib/import/zendesk/ticket/tag.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Import - module Zendesk - class Ticket - class Tag - def initialize(tag, local_ticket, zendesk_ticket) - ::Tag.tag_add( - object: 'Ticket', - o_id: local_ticket.id, - item: tag.id, - created_by_id: Import::Zendesk::UserFactory.local_id(zendesk_ticket.requester_id) || 1, - ) - end - end - end - end -end diff --git a/lib/import/zendesk/ticket/tag_factory.rb b/lib/import/zendesk/ticket/tag_factory.rb deleted file mode 100644 index 7fed1c5d8..000000000 --- a/lib/import/zendesk/ticket/tag_factory.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Import - module Zendesk - class Ticket - module TagFactory - extend Import::Zendesk::Ticket::SubObjectFactory - end - end - end -end diff --git a/lib/import/zendesk/ticket_factory.rb b/lib/import/zendesk/ticket_factory.rb deleted file mode 100644 index ebc313724..000000000 --- a/lib/import/zendesk/ticket_factory.rb +++ /dev/null @@ -1,46 +0,0 @@ -module Import - module Zendesk - module TicketFactory - extend Import::Zendesk::BaseFactory - - # rubocop:disable Style/ModuleFunction - extend self - - private - - def import_loop(records, *args) - - count_update_hook = proc do |record| - yield(record) - update_ticket_count(records) - end - - super(records, *args, &count_update_hook) - end - - def update_ticket_count(collection) - - cache_key = 'import_zendesk_stats' - count_variable = :@count - page_variable = :@next_page - - next_page = collection.instance_variable_get(page_variable) - @last_page ||= next_page - - return if @last_page == next_page - return if !collection.instance_variable_get(count_variable) - - @last_page = next_page - - # check cache - cache = Cache.get(cache_key) - return if !cache - - cache['Tickets'] ||= 0 - cache['Tickets'] += collection.instance_variable_get(count_variable) - - Cache.write(cache_key, cache) - end - end - end -end diff --git a/lib/import/zendesk/ticket_field.rb b/lib/import/zendesk/ticket_field.rb deleted file mode 100644 index 1a84bd374..000000000 --- a/lib/import/zendesk/ticket_field.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Import - module Zendesk - class TicketField < Import::Zendesk::ObjectField - - private - - def remote_name(ticket_field) - ticket_field.title - end - end - end -end diff --git a/lib/import/zendesk/ticket_field_factory.rb b/lib/import/zendesk/ticket_field_factory.rb deleted file mode 100644 index 14138ee8e..000000000 --- a/lib/import/zendesk/ticket_field_factory.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Import - module Zendesk - module TicketFieldFactory - extend Import::Zendesk::BaseFactory - extend Import::Zendesk::LocalIDMapperHook - - MAPPING = { - 'subject' => 'title', - 'description' => 'note', - 'status' => 'state_id', - 'tickettype' => 'type', - 'priority' => 'priority_id', - 'basic_priority' => 'priority_id', - 'group' => 'group_id', - 'assignee' => 'owner_id', - }.freeze - - # rubocop:disable Style/ModuleFunction - extend self - - def skip?(field, *_args) - # check if the Ticket object already has a same named column / attribute - # so we want to skip instead of importing it - ::Ticket.column_names.include?( local_attribute(field) ) - end - - private - - def local_attribute(field) - MAPPING.fetch(field.type, field.type) - end - end - end -end diff --git a/lib/import/zendesk/user.rb b/lib/import/zendesk/user.rb deleted file mode 100644 index bda0b4d8c..000000000 --- a/lib/import/zendesk/user.rb +++ /dev/null @@ -1,75 +0,0 @@ -# Rails autoload has some issues with same namend sub-classes -# in the importer folder require AND simultaniuos requiring -# of the same file in different threads so we need to -# require them ourself -require 'import/zendesk/user/group' -require 'import/zendesk/user/role' - -# https://developer.zendesk.com/rest_api/docs/core/users -module Import - module Zendesk - class User - include Import::Zendesk::Helper - - attr_reader :zendesk_id, :id - - def initialize(user) - local_user = ::User.create_or_update( local_user_fields(user) ) - @zendesk_id = user.id - @id = local_user.id - end - - private - - def local_user_fields(user) - { - login: login(user), - firstname: user.name, - email: user.email, - phone: user.phone, - password: password(user), - active: !user.suspended, - groups: Import::Zendesk::User::Group.for(user), - roles: roles(user), - note: user.notes, - verified: user.verified, - organization_id: Import::Zendesk::OrganizationFactory.local_id( user.organization_id ), - last_login: user.last_login_at, - image_source: photo(user), - updated_by_id: 1, - created_by_id: 1 - }.merge(custom_fields(user)) - end - - def login(user) - return user.email if user.email - # Zendesk users may have no other identifier than the ID, e.g. twitter users - user.id.to_s - end - - def password(user) - return Setting.get('import_zendesk_endpoint_key') if import_user?(user) - '' - end - - def roles(user) - return Import::Zendesk::User::Role.map(user, 'admin') if import_user?(user) - Import::Zendesk::User::Role.for(user) - end - - def import_user?(user) - return false if user.email.blank? - user.email == Setting.get('import_zendesk_endpoint_username') - end - - def photo(user) - return if !user.photo - user.photo.content_url - end - - def custom_fields(user) - get_fields(user.user_fields) - end - end - end -end diff --git a/lib/import/zendesk/user/group.rb b/lib/import/zendesk/user/group.rb deleted file mode 100644 index 9fe4782a8..000000000 --- a/lib/import/zendesk/user/group.rb +++ /dev/null @@ -1,51 +0,0 @@ -# this require is required (hehe) because of Rails autoloading -# which causes strange behavior not inheriting correctly -# from Import::OTRS::DynamicField -require 'import/zendesk/user' - -# https://developer.zendesk.com/rest_api/docs/core/groups -module Import - module Zendesk - class User - module Group - - # rubocop:disable Style/ModuleFunction - extend self - - def for(user) - groups = [] - return groups if mapping[user.id].blank? - - mapping[user.id].each do |zendesk_group_id| - - local_group_id = Import::Zendesk::GroupFactory.local_id(zendesk_group_id) - - next if !local_group_id - - group = ::Group.find( local_group_id ) - - groups.push(group) - end - groups - end - - private - - def mapping - - return @mapping if !@mapping.nil? - - @mapping = {} - - Import::Zendesk::Requester.client.group_memberships.all! do |group_membership| - - @mapping[ group_membership.user_id ] ||= [] - @mapping[ group_membership.user_id ].push( group_membership.group_id ) - end - - @mapping - end - end - end - end -end diff --git a/lib/import/zendesk/user/role.rb b/lib/import/zendesk/user/role.rb deleted file mode 100644 index fdc960c37..000000000 --- a/lib/import/zendesk/user/role.rb +++ /dev/null @@ -1,64 +0,0 @@ -# this require is required (hehe) because of Rails autoloading -# which causes strange behavior not inheriting correctly -# from Import::OTRS::DynamicField -require 'import/zendesk/user' - -module Import - module Zendesk - class User - module Role - extend Import::Helper - - # rubocop:disable Style/ModuleFunction - extend self - - def for(user) - map(user, group_method( user.role.name )) - end - - def map(user, role) - send(role.to_sym, user) - rescue NoMethodError => e - log "Unknown mapping for role '#{user.role.name}' and user with id '#{user.id}'" - [] - end - - private - - def end_user(_user) - [role_customer] - end - - def agent(user) - return [ role_agent ] if user.restricted_agent - admin(user) - end - - def admin(_user) - [role_admin, role_agent] - end - - def group_method(role) - role.tr('-', '_') - end - - def role_admin - @role_admin ||= lookup('Admin') - end - - def role_agent - @role_agent ||= lookup('Agent') - end - - def role_customer - @role_customer ||= lookup('Customer') - end - - def lookup(role_name) - ::Role.lookup(name: role_name) - end - - end - end - end -end diff --git a/lib/import/zendesk/user_factory.rb b/lib/import/zendesk/user_factory.rb deleted file mode 100644 index ec3fafe5b..000000000 --- a/lib/import/zendesk/user_factory.rb +++ /dev/null @@ -1,8 +0,0 @@ -module Import - module Zendesk - module UserFactory - extend Import::Zendesk::BaseFactory - extend Import::Zendesk::LocalIDMapperHook - end - end -end diff --git a/lib/import/zendesk/user_field.rb b/lib/import/zendesk/user_field.rb deleted file mode 100644 index 5b8f615f1..000000000 --- a/lib/import/zendesk/user_field.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Import - module Zendesk - class UserField < Import::Zendesk::ObjectField - end - end -end diff --git a/lib/import/zendesk/user_field_factory.rb b/lib/import/zendesk/user_field_factory.rb deleted file mode 100644 index 101d50178..000000000 --- a/lib/import/zendesk/user_field_factory.rb +++ /dev/null @@ -1,8 +0,0 @@ -module Import - module Zendesk - module UserFieldFactory - extend Import::Zendesk::BaseFactory - extend Import::Zendesk::LocalIDMapperHook - end - end -end diff --git a/lib/sequencer/sequence/import/zendesk/connection_test.rb b/lib/sequencer/sequence/import/zendesk/connection_test.rb new file mode 100644 index 000000000..2e6663567 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/connection_test.rb @@ -0,0 +1,21 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class ConnectionTest < Sequencer::Sequence::Base + + def self.expecting + [:connected] + end + + def self.sequence + [ + 'Zendesk::Client', + 'Zendesk::Connected', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/full.rb b/lib/sequencer/sequence/import/zendesk/full.rb new file mode 100644 index 000000000..969106c11 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/full.rb @@ -0,0 +1,32 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class Full < Sequencer::Sequence::Base + + def self.sequence + [ + 'Import::Common::ImportMode::Check', + 'Import::Common::SystemInitDone::Check', + 'Zendesk::Client', + 'Import::Zendesk::ObjectsTotalCount', + 'Import::Common::ImportJob::Statistics::Update', + 'Import::Common::ImportJob::Statistics::Store', + 'Import::Common::ImportJob::DryRun', + 'Import::Zendesk::Groups', + 'Import::Zendesk::OrganizationFields', + 'Import::Zendesk::Organizations', + 'Import::Zendesk::UserFields', + 'Import::Zendesk::UserGroupMap', + 'Import::Zendesk::Users', + 'Import::Zendesk::TicketFields', + 'Import::Zendesk::Tickets', + 'Import::Common::SystemInitDone::Set', + 'Import::Common::ImportMode::Unset', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/group.rb b/lib/sequencer/sequence/import/zendesk/group.rb new file mode 100644 index 000000000..7feaf1396 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/group.rb @@ -0,0 +1,25 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class Group < Sequencer::Sequence::Base + + def self.sequence + [ + 'Common::ModelClass::Group', + 'Import::Zendesk::Group::Mapping', + 'Import::Common::Model::Attributes::AddByIds', + 'Import::Common::Model::FindBy::Name', + 'Import::Common::Model::Update', + 'Import::Common::Model::Create', + 'Import::Common::Model::Save', + 'Import::Common::Model::Statistics::Diff::ModelKey', + 'Import::Common::ImportJob::Statistics::Update', + 'Import::Common::ImportJob::Statistics::Store', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/organization.rb b/lib/sequencer/sequence/import/zendesk/organization.rb new file mode 100644 index 000000000..9b2aef744 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/organization.rb @@ -0,0 +1,26 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class Organization < Sequencer::Sequence::Base + + def self.sequence + [ + 'Common::ModelClass::Organization', + 'Import::Zendesk::Organization::Mapping', + 'Import::Zendesk::Organization::CustomFields', + 'Import::Common::Model::Attributes::AddByIds', + 'Import::Common::Model::FindBy::Name', + 'Import::Common::Model::Update', + 'Import::Common::Model::Create', + 'Import::Common::Model::Save', + 'Import::Common::Model::Statistics::Diff::ModelKey', + 'Import::Common::ImportJob::Statistics::Update', + 'Import::Common::ImportJob::Statistics::Store', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/organization_field.rb b/lib/sequencer/sequence/import/zendesk/organization_field.rb new file mode 100644 index 000000000..718171ece --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/organization_field.rb @@ -0,0 +1,18 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class OrganizationField < Sequencer::Sequence::Base + + def self.sequence + [ + 'Common::ModelClass::Organization', + 'Import::Zendesk::ObjectAttribute::SanitizedName', + 'Import::Zendesk::ObjectAttribute::Add', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/ticket.rb b/lib/sequencer/sequence/import/zendesk/ticket.rb new file mode 100644 index 000000000..7c9681c10 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/ticket.rb @@ -0,0 +1,37 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class Ticket < Sequencer::Sequence::Base + + def self.sequence + [ + 'Import::Zendesk::Ticket::UserID', + 'Import::Zendesk::Ticket::OwnerID', + 'Import::Zendesk::Ticket::GroupID', + 'Import::Zendesk::Ticket::OrganizationID', + 'Import::Zendesk::Ticket::PriorityID', + 'Import::Zendesk::Ticket::StateID', + 'Import::Zendesk::Common::ArticleSenderID', + 'Import::Zendesk::Common::ArticleTypeID', + 'Import::Zendesk::Ticket::Subject', + 'Import::Zendesk::Ticket::CustomFields', + 'Import::Zendesk::Ticket::Mapping', + 'Common::ModelClass::Ticket', + 'Import::Common::Model::FindBy::Id', + 'Import::Common::Model::Update', + 'Import::Common::Model::Create', + 'Import::Common::Model::Save', + 'Import::Common::Model::ResetPrimaryKeySequence', + 'Import::Zendesk::Ticket::Tags', + 'Import::Zendesk::Ticket::Comments', + 'Import::Common::Model::Statistics::Diff::ModelKey', + 'Import::Common::ImportJob::Statistics::Update', + 'Import::Common::ImportJob::Statistics::Store', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/ticket/comment.rb b/lib/sequencer/sequence/import/zendesk/ticket/comment.rb new file mode 100644 index 000000000..986aa8d04 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/ticket/comment.rb @@ -0,0 +1,30 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class Ticket < Sequencer::Sequence::Base + class Comment < Sequencer::Sequence::Base + + def self.sequence + [ + 'Import::Zendesk::Ticket::Comment::UserID', + 'Import::Zendesk::Common::ArticleSenderID', + 'Import::Zendesk::Common::ArticleTypeID', + 'Import::Zendesk::Ticket::Comment::From', + 'Import::Zendesk::Ticket::Comment::To', + 'Import::Zendesk::Ticket::Comment::Mapping', + 'Import::Zendesk::Ticket::Comment::UnsetInstance', + 'Common::ModelClass::Ticket::Article', + 'Import::Common::Model::FindBy::Id', + 'Import::Common::Model::Update', + 'Import::Common::Model::Create', + 'Import::Common::Model::Save', + 'Import::Zendesk::Ticket::Comment::Attachments', + ] + end + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/ticket/comment/attachment.rb b/lib/sequencer/sequence/import/zendesk/ticket/comment/attachment.rb new file mode 100644 index 000000000..6a0929ede --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/ticket/comment/attachment.rb @@ -0,0 +1,21 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class Ticket < Sequencer::Sequence::Base + class Comment < Sequencer::Sequence::Base + class Attachment < Sequencer::Sequence::Base + + def self.sequence + [ + 'Import::Zendesk::Ticket::Comment::Attachment::Request', + 'Import::Zendesk::Ticket::Comment::Attachment::Add', + ] + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/ticket/tag.rb b/lib/sequencer/sequence/import/zendesk/ticket/tag.rb new file mode 100644 index 000000000..ac3b400b2 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/ticket/tag.rb @@ -0,0 +1,19 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class Ticket < Sequencer::Sequence::Base + class Tag < Sequencer::Sequence::Base + + def self.sequence + [ + 'Import::Zendesk::Ticket::Tag::Item', + 'Common::Tag::Add', + ] + end + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/ticket_field.rb b/lib/sequencer/sequence/import/zendesk/ticket_field.rb new file mode 100644 index 000000000..b7ad03cba --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/ticket_field.rb @@ -0,0 +1,19 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class TicketField < Sequencer::Sequence::Base + + def self.sequence + [ + 'Common::ModelClass::Ticket', + 'Import::Zendesk::TicketField::CheckCustom', + 'Import::Zendesk::TicketField::SanitizedName', + 'Import::Zendesk::TicketField::Add', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/user.rb b/lib/sequencer/sequence/import/zendesk/user.rb new file mode 100644 index 000000000..62d582b39 --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/user.rb @@ -0,0 +1,33 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class User < Sequencer::Sequence::Base + + def self.sequence + [ + 'Import::Zendesk::User::Initiator', + 'Import::Zendesk::User::Roles', + 'Import::Zendesk::User::Groups', + 'Import::Zendesk::User::Login', + 'Import::Zendesk::User::Password', + 'Import::Zendesk::User::ImageSource', + 'Import::Zendesk::User::OrganizationID', + 'Common::ModelClass::User', + 'Import::Zendesk::User::Mapping', + 'Import::Zendesk::User::CustomFields', + 'Import::Common::Model::Attributes::AddByIds', + 'Import::Common::Model::FindBy::UserAttributes', + 'Import::Common::Model::Update', + 'Import::Common::Model::Create', + 'Import::Common::Model::Save', + 'Import::Common::Model::Statistics::Diff::ModelKey', + 'Import::Common::ImportJob::Statistics::Update', + 'Import::Common::ImportJob::Statistics::Store', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/sequence/import/zendesk/user_field.rb b/lib/sequencer/sequence/import/zendesk/user_field.rb new file mode 100644 index 000000000..b4549d1eb --- /dev/null +++ b/lib/sequencer/sequence/import/zendesk/user_field.rb @@ -0,0 +1,18 @@ +class Sequencer + class Sequence + module Import + module Zendesk + class UserField < Sequencer::Sequence::Base + + def self.sequence + [ + 'Common::ModelClass::User', + 'Import::Zendesk::ObjectAttribute::SanitizedName', + 'Import::Zendesk::ObjectAttribute::Add', + ] + end + end + end + end + end +end diff --git a/lib/sequencer/unit/common/model_class/group.rb b/lib/sequencer/unit/common/model_class/group.rb new file mode 100644 index 000000000..537e718a0 --- /dev/null +++ b/lib/sequencer/unit/common/model_class/group.rb @@ -0,0 +1,10 @@ +class Sequencer + class Unit + module Common + module ModelClass + class Group < Sequencer::Unit::Common::ModelClass::Base + end + end + end + end +end diff --git a/lib/sequencer/unit/common/model_class/organization.rb b/lib/sequencer/unit/common/model_class/organization.rb new file mode 100644 index 000000000..9ced58f55 --- /dev/null +++ b/lib/sequencer/unit/common/model_class/organization.rb @@ -0,0 +1,10 @@ +class Sequencer + class Unit + module Common + module ModelClass + class Organization < Sequencer::Unit::Common::ModelClass::Base + end + end + end + end +end diff --git a/lib/sequencer/unit/common/model_class/ticket.rb b/lib/sequencer/unit/common/model_class/ticket.rb new file mode 100644 index 000000000..80cda1dbd --- /dev/null +++ b/lib/sequencer/unit/common/model_class/ticket.rb @@ -0,0 +1,10 @@ +class Sequencer + class Unit + module Common + module ModelClass + class Ticket < Sequencer::Unit::Common::ModelClass::Base + end + end + end + end +end diff --git a/lib/sequencer/unit/common/model_class/ticket/article.rb b/lib/sequencer/unit/common/model_class/ticket/article.rb new file mode 100644 index 000000000..86de53c2a --- /dev/null +++ b/lib/sequencer/unit/common/model_class/ticket/article.rb @@ -0,0 +1,12 @@ +class Sequencer + class Unit + module Common + module ModelClass + class Ticket < Sequencer::Unit::Common::ModelClass::Base + class Article < Sequencer::Unit::Common::ModelClass::Base + end + end + end + end + end +end diff --git a/lib/sequencer/unit/common/tag/add.rb b/lib/sequencer/unit/common/tag/add.rb new file mode 100644 index 000000000..38d9b1181 --- /dev/null +++ b/lib/sequencer/unit/common/tag/add.rb @@ -0,0 +1,21 @@ +class Sequencer + class Unit + module Common + module Tag + class Add < Sequencer::Unit::Base + + uses :model_class, :instance, :item, :user_id + + def process + ::Tag.tag_add( + object: model_class.name, + o_id: instance.id, + item: item, + created_by_id: user_id, + ) + end + end + end + end + end +end diff --git a/lib/sequencer/unit/common/unset_attributes.rb b/lib/sequencer/unit/common/unset_attributes.rb new file mode 100644 index 000000000..86576281f --- /dev/null +++ b/lib/sequencer/unit/common/unset_attributes.rb @@ -0,0 +1,17 @@ +class Sequencer + class Unit + module Common + class UnsetAttributes < Sequencer::Unit::Base + + def process + uses = self.class.uses + return if uses.blank? + + uses.each do |attribute| + state.unset(attribute) + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/import_mode/check.rb b/lib/sequencer/unit/import/common/import_mode/check.rb new file mode 100644 index 000000000..288a14bf3 --- /dev/null +++ b/lib/sequencer/unit/import/common/import_mode/check.rb @@ -0,0 +1,18 @@ +class Sequencer + class Unit + module Import + module Common + module ImportMode + class Check < Sequencer::Unit::Base + + def process + # check if system is in import mode + return if Setting.get('import_mode') + raise 'System is not in import mode!' + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/import_mode/unset.rb b/lib/sequencer/unit/import/common/import_mode/unset.rb new file mode 100644 index 000000000..dfc37cdc2 --- /dev/null +++ b/lib/sequencer/unit/import/common/import_mode/unset.rb @@ -0,0 +1,16 @@ +class Sequencer + class Unit + module Import + module Common + module ImportMode + class Unset < Sequencer::Unit::Base + + def process + Setting.set('import_mode', false) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/model/find_by/id.rb b/lib/sequencer/unit/import/common/model/find_by/id.rb new file mode 100644 index 000000000..403e96751 --- /dev/null +++ b/lib/sequencer/unit/import/common/model/find_by/id.rb @@ -0,0 +1,14 @@ +class Sequencer + class Unit + module Import + module Common + module Model + module FindBy + class Id < Sequencer::Unit::Import::Common::Model::FindBy::SameNamedAttribute + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/model/find_by/name.rb b/lib/sequencer/unit/import/common/model/find_by/name.rb new file mode 100644 index 000000000..81e4fd11c --- /dev/null +++ b/lib/sequencer/unit/import/common/model/find_by/name.rb @@ -0,0 +1,14 @@ +class Sequencer + class Unit + module Import + module Common + module Model + module FindBy + class Name < Sequencer::Unit::Import::Common::Model::FindBy::SameNamedAttribute + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/model/find_by/same_named_attribute.rb b/lib/sequencer/unit/import/common/model/find_by/same_named_attribute.rb new file mode 100644 index 000000000..8d939637e --- /dev/null +++ b/lib/sequencer/unit/import/common/model/find_by/same_named_attribute.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Common + module Model + module FindBy + class SameNamedAttribute < Sequencer::Unit::Import::Common::Model::Lookup::Attributes + + private + + def attribute + self.class.name.demodulize.underscore.to_sym + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/model/find_by/user_attributes.rb b/lib/sequencer/unit/import/common/model/find_by/user_attributes.rb new file mode 100644 index 000000000..c91b7f752 --- /dev/null +++ b/lib/sequencer/unit/import/common/model/find_by/user_attributes.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Common + module Model + module FindBy + class UserAttributes < Sequencer::Unit::Import::Common::Model::Lookup::Attributes + + private + + def attributes + %i[login email] + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/model/reset_primary_key_sequence.rb b/lib/sequencer/unit/import/common/model/reset_primary_key_sequence.rb new file mode 100644 index 000000000..b5e3eaee6 --- /dev/null +++ b/lib/sequencer/unit/import/common/model/reset_primary_key_sequence.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Common + module Model + class ResetPrimaryKeySequence < Sequencer::Unit::Base + + uses :model_class + + delegate table_name: :model_class + + def process + DbHelper.import_post(table_name) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/model/statistics/mixin/common.rb b/lib/sequencer/unit/import/common/model/statistics/mixin/common.rb index 7d7202624..0091c3493 100644 --- a/lib/sequencer/unit/import/common/model/statistics/mixin/common.rb +++ b/lib/sequencer/unit/import/common/model/statistics/mixin/common.rb @@ -12,12 +12,16 @@ class Sequencer %i[skipped created updated unchanged failed deactivated] end + def results + %i[sum total] + end + def empty_diff possible_actions.collect { |key| [key, 0] }.to_h end def possible_actions - @possible_actions ||= actions + @possible_actions ||= actions + results end end end diff --git a/lib/sequencer/unit/import/common/model/statistics/total.rb b/lib/sequencer/unit/import/common/model/statistics/total.rb new file mode 100644 index 000000000..772619e73 --- /dev/null +++ b/lib/sequencer/unit/import/common/model/statistics/total.rb @@ -0,0 +1,29 @@ +class Sequencer + class Unit + module Import + module Common + module Model + module Statistics + class Total < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Common::Model::Statistics::Mixin::EmptyDiff + + def process + state.provide(:statistics_diff) do + diff.merge( + total: total + ) + end + end + + private + + def total + raise "Missing implementation if total method for class #{self.class.name}" + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/object_attribute/sanitized_name.rb b/lib/sequencer/unit/import/common/object_attribute/sanitized_name.rb new file mode 100644 index 000000000..4afd75537 --- /dev/null +++ b/lib/sequencer/unit/import/common/object_attribute/sanitized_name.rb @@ -0,0 +1,46 @@ +class Sequencer + class Unit + module Import + module Common + module ObjectAttribute + class SanitizedName < Sequencer::Unit::Common::Provider::Named + + private + + def sanitized_name + # model_no + # model_nos + # model_name + # model_name + without_double_underscores.gsub(/_id(s?)$/, '_no\1') + end + + def without_double_underscores + # model_id + # model_ids + # model_name + # model_name + without_spaces_and_slashes.gsub(/_{2,}/, '_') + end + + def without_spaces_and_slashes + # model_id + # model_ids + # model___name + # model_name + unsanitized_name.gsub(%r{[\s\/]}, '_').underscore + end + + def unsanitized_name + # Model ID + # Model IDs + # Model / Name + # Model Name + raise 'Missing implementation for unsanitized_name method' + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/system_init_done/check.rb b/lib/sequencer/unit/import/common/system_init_done/check.rb new file mode 100644 index 000000000..a3dac83c7 --- /dev/null +++ b/lib/sequencer/unit/import/common/system_init_done/check.rb @@ -0,0 +1,17 @@ +class Sequencer + class Unit + module Import + module Common + module SystemInitDone + class Check < Sequencer::Unit::Base + + def process + return if !Setting.get('system_init_done') + raise 'System is already system_init_done!' + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/common/system_init_done/set.rb b/lib/sequencer/unit/import/common/system_init_done/set.rb new file mode 100644 index 000000000..73c7a43ab --- /dev/null +++ b/lib/sequencer/unit/import/common/system_init_done/set.rb @@ -0,0 +1,16 @@ +class Sequencer + class Unit + module Import + module Common + module SystemInitDone + class Set < Sequencer::Unit::Base + + def process + Setting.set('system_init_done', true) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/common/article_sender_id.rb b/lib/sequencer/unit/import/zendesk/common/article_sender_id.rb new file mode 100644 index 000000000..d6e9f9b3f --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/common/article_sender_id.rb @@ -0,0 +1,30 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Common + class ArticleSenderID < Sequencer::Unit::Common::Provider::Named + + uses :user_id + + private + + def article_sender_id + return article_sender('Customer') if author.role?('Customer') + return article_sender('Agent') if author.role?('Agent') + article_sender('System') + end + + def author + @author ||= ::User.find(user_id) + end + + def article_sender(name) + ::Ticket::Article::Sender.select(:id).find_by(name: name).id + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/common/article_type_id.rb b/lib/sequencer/unit/import/zendesk/common/article_type_id.rb new file mode 100644 index 000000000..918a2d623 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/common/article_type_id.rb @@ -0,0 +1,52 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Common + class ArticleTypeID < Sequencer::Unit::Common::Provider::Named + + uses :resource + + private + + def article_type_id + ::Ticket::Article::Type.select(:id).find_by(name: name).id + end + + def name + known_channel || 'web' + end + + def known_channel + channel = resource.via.channel + direct_mapping.fetch(channel, indirect_map(channel)) + end + + def indirect_map(channel) + method_name = "remote_name_#{channel}".to_sym + send(method_name) if respond_to?(method_name, true) + end + + def remote_name_facebook + return 'facebook feed post' if resource.via.source.rel == 'post' + 'facebook feed comment' + end + + def remote_name_twitter + return 'twitter status' if resource.via.source.rel == 'mention' + 'twitter direct message' + end + + def direct_mapping + { + 'web' => 'web', + 'email' => 'email', + 'sample_ticket' => 'note', + }.freeze + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/common/custom_fields.rb b/lib/sequencer/unit/import/zendesk/common/custom_fields.rb new file mode 100644 index 000000000..beec7436c --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/common/custom_fields.rb @@ -0,0 +1,39 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Common + class CustomFields < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Common::Mapping::Mixin::ProvideMapped + + uses :resource + + def process + provide_mapped do + attributes_hash + end + end + + private + + def remote_fields + raise 'Missing implementation of remote_fields method' + end + + def fields + @fields ||= remote_fields + end + + def attributes_hash + return {} if fields.blank? + fields.each_with_object({}) do |(key, value), result| + next if value.nil? + result[key] = value + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/group/mapping.rb b/lib/sequencer/unit/import/zendesk/group/mapping.rb new file mode 100644 index 000000000..14e0992a9 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/group/mapping.rb @@ -0,0 +1,24 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Group + class Mapping < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Common::Mapping::Mixin::ProvideMapped + + uses :resource + + def process + provide_mapped do + { + name: resource.name, + active: !resource.deleted, + } + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/groups.rb b/lib/sequencer/unit/import/zendesk/groups.rb new file mode 100644 index 000000000..705621390 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/groups.rb @@ -0,0 +1,10 @@ +class Sequencer + class Unit + module Import + module Zendesk + class Groups < Sequencer::Unit::Import::Zendesk::SubSequence::Object + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/object_attribute/add.rb b/lib/sequencer/unit/import/zendesk/object_attribute/add.rb new file mode 100644 index 000000000..1fd67e150 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/object_attribute/add.rb @@ -0,0 +1,25 @@ +class Sequencer + class Unit + module Import + module Zendesk + module ObjectAttribute + class Add < Sequencer::Unit::Base + + uses :model_class, :sanitized_name, :resource + provides :instance + + def process + state.provide(:instance) do + backend_class.new(model_class, sanitized_name, resource) + end + end + + def backend_class + "Import::Zendesk::ObjectAttribute::#{resource.type.capitalize}".constantize + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/object_attribute/sanitized_name.rb b/lib/sequencer/unit/import/zendesk/object_attribute/sanitized_name.rb new file mode 100644 index 000000000..cfc9f10a9 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/object_attribute/sanitized_name.rb @@ -0,0 +1,24 @@ +class Sequencer + class Unit + module Import + module Zendesk + module ObjectAttribute + class SanitizedName < Sequencer::Unit::Import::Common::ObjectAttribute::SanitizedName + + uses :resource + + private + + def unsanitized_name + # Model ID + # Model IDs + # Model / Name + # Model Name + resource['key'] + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/objects_total_count.rb b/lib/sequencer/unit/import/zendesk/objects_total_count.rb new file mode 100644 index 000000000..44294c24e --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/objects_total_count.rb @@ -0,0 +1,50 @@ +class Sequencer + class Unit + module Import + module Zendesk + class ObjectsTotalCount < Sequencer::Unit::Common::Provider::Attribute + include ::Sequencer::Unit::Import::Common::Model::Statistics::Mixin::EmptyDiff + + uses :client + + private + + def statistics_diff + %i[Groups Users Organizations Tickets].each_with_object({}) do |object, stats| + stats[object] = empty_diff.merge( + total: request(object).count! + ) + end + end + + def request(object) + return tickets if object == 'Tickets' + generic(object) + end + + def generic(object) + client.send(object.to_s.underscore.to_sym) + end + + # this special ticket logic is needed since Zendesk archives tickets + # after 120 days and doesn't return them via the client.tickets + # endpoint as described here: + # https://github.com/zammad/zammad/issues/558#issuecomment-267951351 + # the proper way is to use the 'incremental' endpoint which is not available + # via the ruby gem yet but a pull request is pending: + # https://github.com/zendesk/zendesk_api_client_rb/pull/287 + # the following workaround is needed to use this functionality + # Counting Tickets has the limitations that max. 1000 are returned + # that's why we need to update the number when it's exceeded while importing + def tickets + ZendeskAPI::Collection.new( + client, + ZendeskAPI::Ticket, + path: 'incremental/tickets?start_time=1' + ) + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/organization/custom_fields.rb b/lib/sequencer/unit/import/zendesk/organization/custom_fields.rb new file mode 100644 index 000000000..54f4461ec --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/organization/custom_fields.rb @@ -0,0 +1,18 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Organization + class CustomFields < Sequencer::Unit::Import::Zendesk::Common::CustomFields + + private + + def remote_fields + resource.organization_fields + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/organization/mapping.rb b/lib/sequencer/unit/import/zendesk/organization/mapping.rb new file mode 100644 index 000000000..df7273637 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/organization/mapping.rb @@ -0,0 +1,25 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Organization + class Mapping < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Common::Mapping::Mixin::ProvideMapped + + uses :resource + + def process + provide_mapped do + { + name: resource.name, + note: resource.note, + shared: resource.shared_tickets, + } + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/organization_fields.rb b/lib/sequencer/unit/import/zendesk/organization_fields.rb new file mode 100644 index 000000000..49e861a1a --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/organization_fields.rb @@ -0,0 +1,10 @@ +class Sequencer + class Unit + module Import + module Zendesk + class OrganizationFields < Sequencer::Unit::Import::Zendesk::SubSequence::ObjectFields + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/organizations.rb b/lib/sequencer/unit/import/zendesk/organizations.rb new file mode 100644 index 000000000..2f06d1040 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/organizations.rb @@ -0,0 +1,16 @@ +class Sequencer + class Unit + module Import + module Zendesk + class Organizations < Sequencer::Unit::Import::Zendesk::SubSequence::Object + + private + + def resource_iteration_method + :all! + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/sub_sequence/base.rb b/lib/sequencer/unit/import/zendesk/sub_sequence/base.rb new file mode 100644 index 000000000..758f3fa3b --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/sub_sequence/base.rb @@ -0,0 +1,60 @@ +class Sequencer + class Unit + module Import + module Zendesk + module SubSequence + module Base + module ClassMethods + + def resource_klass + @resource_klass ||= name.split('::').last.singularize + end + end + + def self.included(base) + base.extend(ClassMethods) + base.uses :dry_run, :import_job + end + + private + + def default_params + { + dry_run: dry_run, + import_job: import_job, + } + end + + def resource_klass + # base.instance_delegate [:resource_klass] => base + # doesn't work since we are included and then inherited + # there might be multiple inherited hooks which overwrite + # each other :/ + self.class.resource_klass + end + + def sequence_name + "Import::Zendesk::#{resource_klass}" + end + + def resource_iteration(&block) + resource_collection.public_send(resource_iteration_method, &block) + end + + def resource_collection + collection_provider.public_send(resource_collection_attribute) + end + + def resource_iteration_method + :all! + end + + def resource_collection_attribute + @resource_collection_attribute ||= resource_klass.pluralize.underscore + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/sub_sequence/mapped.rb b/lib/sequencer/unit/import/zendesk/sub_sequence/mapped.rb new file mode 100644 index 000000000..1c3d1d111 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/sub_sequence/mapped.rb @@ -0,0 +1,80 @@ +class Sequencer + class Unit + module Import + module Zendesk + module SubSequence + module Mapped + module ClassMethods + + def resource_map + "#{resource_klass.underscore}_map".to_sym + end + + def inherited(base) + base.provides(base.resource_map) + + base.extend(Forwardable) + base.instance_delegate [:resource_map] => base + end + end + + def self.included(base) + base.uses :client + + base.extend(ClassMethods) + end + + def process + state.provide(resource_map) do + process_sub_sequence + mapping + end + end + + private + + def expecting + raise 'Missing implementation of expecting method' + end + + def collection_provider + client + end + + def process_sub_sequence + resource_iteration do |resource| + + expected_value = expected(resource) + + next if expected_value.blank? + + mapping[resource.id] = mapping_value(expected_value) + end + end + + def expected(resource) + result = sub_sequence(resource) + result[expecting] + end + + def sub_sequence(resource) + ::Sequencer.process(sequence_name, + parameters: default_params.merge( + resource: resource + ), + expecting: [expecting]) + end + + def mapping_value(expected_value) + expected_value + end + + def mapping + @mapping ||= {} + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/sub_sequence/object.rb b/lib/sequencer/unit/import/zendesk/sub_sequence/object.rb new file mode 100644 index 000000000..a73d35bbf --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/sub_sequence/object.rb @@ -0,0 +1,24 @@ +class Sequencer + class Unit + module Import + module Zendesk + module SubSequence + class Object < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Zendesk::SubSequence::Base + include ::Sequencer::Unit::Import::Zendesk::SubSequence::Mapped + + private + + def expecting + :instance + end + + def mapping_value(expected_value) + expected_value.id + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/sub_sequence/object_fields.rb b/lib/sequencer/unit/import/zendesk/sub_sequence/object_fields.rb new file mode 100644 index 000000000..c3dc1eedb --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/sub_sequence/object_fields.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module SubSequence + class ObjectFields < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Zendesk::SubSequence::Base + include ::Sequencer::Unit::Import::Zendesk::SubSequence::Mapped + + private + + def expecting + :sanitized_name + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/sub_sequence/sub_object.rb b/lib/sequencer/unit/import/zendesk/sub_sequence/sub_object.rb new file mode 100644 index 000000000..12ce1ee8f --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/sub_sequence/sub_object.rb @@ -0,0 +1,39 @@ +class Sequencer + class Unit + module Import + module Zendesk + module SubSequence + class SubObject < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Zendesk::SubSequence::Base + + uses :resource, :instance, :user_id, :model_class + + def process + resource_iteration do |sub_resource| + + ::Sequencer.process(sequence_name, + parameters: default_params.merge( + resource: sub_resource + ),) + end + end + + private + + def collection_provider + resource + end + + def default_params + super.merge( + instance: instance, + user_id: user_id, + model_class: model_class, + ) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/sub_sequence/ticket_sub_object.rb b/lib/sequencer/unit/import/zendesk/sub_sequence/ticket_sub_object.rb new file mode 100644 index 000000000..30e752462 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/sub_sequence/ticket_sub_object.rb @@ -0,0 +1,18 @@ +class Sequencer + class Unit + module Import + module Zendesk + module SubSequence + class TicketSubObject < Sequencer::Unit::Import::Zendesk::SubSequence::SubObject + + private + + def sequence_name + "Import::Zendesk::Ticket::#{resource_klass}" + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/attachment/add.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/attachment/add.rb new file mode 100644 index 000000000..abe9fadab --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/attachment/add.rb @@ -0,0 +1,37 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + module Attachment + class Add < Sequencer::Unit::Base + prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action + include ::Sequencer::Unit::Import::Common::Model::Mixin::HandleFailure + + skip_action :skipped + + uses :instance, :resource, :response, :model_class + + def process + ::Store.add( + object: model_class.name, + o_id: instance.id, + data: response.body, + filename: resource.file_name, + preferences: { + 'Content-Type' => resource.content_type + }, + created_by_id: 1 + ) + rescue => e + handle_failure(e) + end + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/attachment/request.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/attachment/request.rb new file mode 100644 index 000000000..b7322ca69 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/attachment/request.rb @@ -0,0 +1,51 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + module Attachment + class Request < Sequencer::Unit::Base + prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action + + skip_action :skipped + + uses :resource + provides :response, :action + + def process + if failed? + state.provide(:action, :skipped) + else + state.provide(:response, response) + end + end + + private + + def failed? + return false if response.success? + logger.error response.error + true + end + + def response + @response ||= begin + UserAgent.get( + resource.content_url, + {}, + { + open_timeout: 10, + read_timeout: 60, + }, + ) + end + end + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/attachments.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/attachments.rb new file mode 100644 index 000000000..f3d318476 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/attachments.rb @@ -0,0 +1,58 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + class Attachments < Sequencer::Unit::Import::Zendesk::SubSequence::SubObject + + def process + # check if we need to import the attachments + return if skip? + # if so call the original .process from SubObject class + super + end + + private + + # for better readability + alias remote_attachments resource_collection + + # for better readability + def local_attachments + instance.attachments + end + + def skip? + ensure_common_ground + attachments_equal? + end + + def ensure_common_ground + return if common_ground? + local_attachments.each(&:delete) + end + + def common_ground? + return false if remote_attachments.blank? + attachments_equal? + end + + def attachments_equal? + remote_attachments.count == local_attachments.count + end + + def sequence_name + "Import::Zendesk::Ticket::Comment::#{resource_klass}" + end + + def resource_iteration_method + :each + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/from.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/from.rb new file mode 100644 index 000000000..72f13b09a --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/from.rb @@ -0,0 +1,24 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + class From < Sequencer::Unit::Import::Zendesk::Ticket::Comment::SourceBased + + private + + def email + resource.via.source.from.address + end + + def facebook + resource.via.source.from.facebook_id + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/mapping.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/mapping.rb new file mode 100644 index 000000000..0f728ba17 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/mapping.rb @@ -0,0 +1,36 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + class Mapping < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Common::Mapping::Mixin::ProvideMapped + + uses :instance, :resource, :user_id, :from, :to, :article_sender_id, :article_type_id + provides :mapped + + def process + provide_mapped do + { + from: from, + to: to, + ticket_id: instance.id, + body: resource.html_body, + content_type: 'text/html', + internal: !resource.public, + message_id: resource.id, + updated_by_id: user_id, + created_by_id: user_id, + sender_id: article_sender_id, + type_id: article_type_id, + } + end + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/source_based.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/source_based.rb new file mode 100644 index 000000000..ee130de05 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/source_based.rb @@ -0,0 +1,22 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + class SourceBased < Sequencer::Unit::Common::Provider::Named + + uses :resource + + def value + method_name = resource.via.channel.to_sym + return if !respond_to?(method_name, true) + send(method_name) + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/to.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/to.rb new file mode 100644 index 000000000..e595f94e3 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/to.rb @@ -0,0 +1,25 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + class To < Sequencer::Unit::Import::Zendesk::Ticket::Comment::SourceBased + + private + + def email + # Notice resource.via.from.original_recipients = [\"another@gmail.com\", \"support@example.zendesk.com\"] + resource.via.source.to.address + end + + def facebook + resource.via.source.to.facebook_id + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/unset_instance.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/unset_instance.rb new file mode 100644 index 000000000..063444ddd --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/unset_instance.rb @@ -0,0 +1,16 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + class UnsetInstance < Sequencer::Unit::Common::UnsetAttributes + + uses :instance + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comment/user_id.rb b/lib/sequencer/unit/import/zendesk/ticket/comment/user_id.rb new file mode 100644 index 000000000..fc5e8f472 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comment/user_id.rb @@ -0,0 +1,23 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Comment + class UserID < Sequencer::Unit::Base + + uses :resource, :user_map + provides :user_id + + def process + state.provide(:user_id) do + user_map.fetch(resource.author_id, 1) + end + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/comments.rb b/lib/sequencer/unit/import/zendesk/ticket/comments.rb new file mode 100644 index 000000000..469a4bac8 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/comments.rb @@ -0,0 +1,22 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class Comments < Sequencer::Unit::Import::Zendesk::SubSequence::TicketSubObject + + uses :user_map + + private + + def default_params + super.merge( + user_map: user_map, + ) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/custom_fields.rb b/lib/sequencer/unit/import/zendesk/ticket/custom_fields.rb new file mode 100644 index 000000000..fb6970c03 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/custom_fields.rb @@ -0,0 +1,28 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class CustomFields < Sequencer::Unit::Import::Zendesk::Common::CustomFields + + uses :ticket_field_map + + private + + def remote_fields + custom_fields = resource.custom_fields + return {} if custom_fields.blank? + + custom_fields.map do |custom_field| + [ + ticket_field_map[ custom_field['id'] ].to_sym, # remote_name + custom_field['value'] + ] + end.to_h + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/group_id.rb b/lib/sequencer/unit/import/zendesk/ticket/group_id.rb new file mode 100644 index 000000000..5ae1e5617 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/group_id.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class GroupID < Sequencer::Unit::Common::Provider::Named + + uses :resource, :group_map + + private + + def group_id + group_map.fetch(resource.group_id, 1) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/mapping.rb b/lib/sequencer/unit/import/zendesk/ticket/mapping.rb new file mode 100644 index 000000000..bf1e5cebd --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/mapping.rb @@ -0,0 +1,41 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class Mapping < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Common::Mapping::Mixin::ProvideMapped + + uses :resource, :user_id, :owner_id, :group_id, + :organization_id, :priority_id, :state_id, + :article_sender_id, :article_type_id, + :subject + + def process + provide_mapped do + { + id: resource.id, + title: subject, + owner_id: owner_id, + note: resource.description, + group_id: group_id, + customer_id: user_id, + organization_id: organization_id, + priority_id: priority_id, + state_id: state_id, + pending_time: resource.due_at, + updated_at: resource.updated_at, + created_at: resource.created_at, + updated_by_id: user_id, + created_by_id: user_id, + create_article_sender_id: article_sender_id, + create_article_type_id: article_type_id, + } + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/organization_id.rb b/lib/sequencer/unit/import/zendesk/ticket/organization_id.rb new file mode 100644 index 000000000..514b5a888 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/organization_id.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class OrganizationID < Sequencer::Unit::Common::Provider::Named + + uses :resource, :organization_map + + private + + def organization_id + organization_map[resource.organization_id] + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/owner_id.rb b/lib/sequencer/unit/import/zendesk/ticket/owner_id.rb new file mode 100644 index 000000000..232ac6130 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/owner_id.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class OwnerID < Sequencer::Unit::Common::Provider::Named + + uses :resource, :user_map + + private + + def owner_id + user_map.fetch(resource.assignee, 1) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/priority_id.rb b/lib/sequencer/unit/import/zendesk/ticket/priority_id.rb new file mode 100644 index 000000000..7d19a8707 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/priority_id.rb @@ -0,0 +1,34 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class PriorityID < Sequencer::Unit::Common::Provider::Named + + uses :resource + + private + + def priority_id + ::Ticket::Priority.select(:id).find_by(name: local).id + end + + def local + mapping.fetch(resource.priority, mapping[nil]) + end + + def mapping + { + 'low' => '1 low', + nil => '2 normal', + 'normal' => '2 normal', + 'high' => '3 high', + 'urgent' => '3 high', + }.freeze + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/state_id.rb b/lib/sequencer/unit/import/zendesk/ticket/state_id.rb new file mode 100644 index 000000000..e6336a955 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/state_id.rb @@ -0,0 +1,32 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class StateID < Sequencer::Unit::Common::Provider::Named + + uses :resource + + private + + def state_id + ::Ticket::State.select(:id).find_by(name: local).id + end + + def local + mapping.fetch(resource.status, resource.status) + end + + def mapping + { + 'pending' => 'pending reminder', + 'solved' => 'closed', + 'deleted' => 'removed', + }.freeze + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/subject.rb b/lib/sequencer/unit/import/zendesk/ticket/subject.rb new file mode 100644 index 000000000..f4a8dd29a --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/subject.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class Subject < Sequencer::Unit::Common::Provider::Named + + uses :resource + + private + + def subject + resource.subject || resource.description || '-' + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/tag/item.rb b/lib/sequencer/unit/import/zendesk/ticket/tag/item.rb new file mode 100644 index 000000000..ac9521da4 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/tag/item.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + module Tag + class Item < Sequencer::Unit::Common::Provider::Named + + uses :resource + + def item + resource.id + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/tags.rb b/lib/sequencer/unit/import/zendesk/ticket/tags.rb new file mode 100644 index 000000000..a91fd04c8 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/tags.rb @@ -0,0 +1,12 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class Tags < Sequencer::Unit::Import::Zendesk::SubSequence::TicketSubObject + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket/user_id.rb b/lib/sequencer/unit/import/zendesk/ticket/user_id.rb new file mode 100644 index 000000000..84d8d9bcf --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket/user_id.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module Ticket + class UserID < Sequencer::Unit::Common::Provider::Named + + uses :resource, :user_map + + private + + def user_id + user_map.fetch(resource.requester_id, 1) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket_field/add.rb b/lib/sequencer/unit/import/zendesk/ticket_field/add.rb new file mode 100644 index 000000000..0522ec456 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket_field/add.rb @@ -0,0 +1,17 @@ +class Sequencer + class Unit + module Import + module Zendesk + module TicketField + class Add < Sequencer::Unit::Import::Zendesk::ObjectAttribute::Add + prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action + + skip_action :skipped + + uses :action + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket_field/check_custom.rb b/lib/sequencer/unit/import/zendesk/ticket_field/check_custom.rb new file mode 100644 index 000000000..2a5e03a74 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket_field/check_custom.rb @@ -0,0 +1,43 @@ +class Sequencer + class Unit + module Import + module Zendesk + module TicketField + class CheckCustom < Sequencer::Unit::Base + + uses :resource, :model_class + provides :action + + def process + return if custom? + state.provide(:action, :skipped) + end + + private + + def custom? + model_class.column_names.exclude?(attribute) + end + + def attribute + mapping.fetch(resource.type, resource.type) + end + + def mapping + { + 'subject' => 'title', + 'description' => 'note', + 'status' => 'state_id', + 'tickettype' => 'type', + 'priority' => 'priority_id', + 'basic_priority' => 'priority_id', + 'group' => 'group_id', + 'assignee' => 'owner_id', + }.freeze + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket_field/sanitized_name.rb b/lib/sequencer/unit/import/zendesk/ticket_field/sanitized_name.rb new file mode 100644 index 000000000..e35ba2ce9 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket_field/sanitized_name.rb @@ -0,0 +1,27 @@ +class Sequencer + class Unit + module Import + module Zendesk + module TicketField + class SanitizedName < Sequencer::Unit::Import::Common::ObjectAttribute::SanitizedName + prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action + + skip_action :skipped + + uses :resource, :action + + private + + def unsanitized_name + # Model ID + # Model IDs + # Model / Name + # Model Name + resource.title + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/ticket_fields.rb b/lib/sequencer/unit/import/zendesk/ticket_fields.rb new file mode 100644 index 000000000..01ab5d93c --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/ticket_fields.rb @@ -0,0 +1,10 @@ +class Sequencer + class Unit + module Import + module Zendesk + class TicketFields < Sequencer::Unit::Import::Zendesk::SubSequence::ObjectFields + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/tickets.rb b/lib/sequencer/unit/import/zendesk/tickets.rb new file mode 100644 index 000000000..075ee4772 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/tickets.rb @@ -0,0 +1,77 @@ +class Sequencer + class Unit + module Import + module Zendesk + class Tickets < Sequencer::Unit::Import::Zendesk::SubSequence::Object + + uses :user_map, :organization_map, :group_map, :ticket_field_map + + private + + def default_params + super.merge( + user_map: user_map, + group_map: group_map, + organization_map: organization_map, + ticket_field_map: ticket_field_map, + ) + end + + def resource_iteration + super do |record| + # call passed/originally intended block + yield(record) + + # add hook to check if ticket count + # update is needed because the request + # might have changed + update_ticket_count + end + end + + # The source if this is the limitation of not knowing + # how much tickets there are in total before requesting the endpoint + # This is caused by the Zendesk API which only returns max. 1000 + # per request + def update_ticket_count + update_import_job + previous_page = next_page + end + + attr_accessor :previous_page + + def update_import_job + return if !update_required? + state.provide(import_job, updated_import_job) + end + + def updated_import_job + import_job.result[:Tickets].merge( + total: import_job.result[:Tickets][:total] + current_request_count + ) + end + + def update_required? + return false if previous_page.blank? + return false if previous_page == next_page + current_request_count.present? + end + + def current_request_count + # access the internal instance method of the + # Zendesk collection request to get the current + # count of the endpoint (max. 1000) + resource_collection_attribute.instance_variable_get(:@count) + end + + def next_page + # access the internal instance method of the + # Zendesk collection request to get the next + # page number of the endpoint + resource_collection_attribute.instance_variable_get(:@next_page) + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/custom_fields.rb b/lib/sequencer/unit/import/zendesk/user/custom_fields.rb new file mode 100644 index 000000000..2b9986e98 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/custom_fields.rb @@ -0,0 +1,18 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class CustomFields < Sequencer::Unit::Import::Zendesk::Common::CustomFields + + private + + def remote_fields + resource.user_fields + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/groups.rb b/lib/sequencer/unit/import/zendesk/user/groups.rb new file mode 100644 index 000000000..a3a5d9f50 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/groups.rb @@ -0,0 +1,27 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class Groups < Sequencer::Unit::Common::Provider::Named + + uses :resource, :group_map, :user_group_map + + private + + def groups + remote_ids.map { |remote_id| group_map[remote_id] } + .compact + .map { |local_id| ::Group.find(local_id) } + end + + def remote_ids + return [] if user_group_map.blank? + user_group_map.fetch(resource.id, []) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/image_source.rb b/lib/sequencer/unit/import/zendesk/user/image_source.rb new file mode 100644 index 000000000..1a68db5ce --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/image_source.rb @@ -0,0 +1,20 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class ImageSource < Sequencer::Unit::Common::Provider::Named + + uses :resource + + private + + def image_source + resource&.photo&.content_url + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/initiator.rb b/lib/sequencer/unit/import/zendesk/user/initiator.rb new file mode 100644 index 000000000..8f91258b8 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/initiator.rb @@ -0,0 +1,26 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class Initiator < Sequencer::Unit::Base + + uses :resource + provides :initiator + + def process + state.provide(:initiator, initiator?) + end + + private + + def initiator? + return false if resource.email.blank? + resource.email == Setting.get('import_zendesk_endpoint_username') + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/login.rb b/lib/sequencer/unit/import/zendesk/user/login.rb new file mode 100644 index 000000000..d03d8c5fc --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/login.rb @@ -0,0 +1,21 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class Login < Sequencer::Unit::Common::Provider::Named + + uses :resource + + private + + def login + # Zendesk users may have no other identifier than the ID, e.g. twitter users + resource.email || resource.id.to_s + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/mapping.rb b/lib/sequencer/unit/import/zendesk/user/mapping.rb new file mode 100644 index 000000000..0f69176bf --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/mapping.rb @@ -0,0 +1,35 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class Mapping < Sequencer::Unit::Base + include ::Sequencer::Unit::Import::Common::Mapping::Mixin::ProvideMapped + + uses :resource, :login, :password, :roles, :groups, :organization_id, :image_source + + def process + provide_mapped do + { + login: login, + firstname: resource.name, + email: resource.email, + phone: resource.phone, + password: password, + active: !resource.suspended, + groups: groups, + roles: roles, + note: resource.notes, + verified: resource.verified, + organization_id: organization_id, + last_login: resource.last_login_at, + image_source: image_source, + } + end + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/organization_id.rb b/lib/sequencer/unit/import/zendesk/user/organization_id.rb new file mode 100644 index 000000000..5fd7708f4 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/organization_id.rb @@ -0,0 +1,22 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class OrganizationID < Sequencer::Unit::Common::Provider::Named + + uses :resource, :organization_map + + private + + def organization_id + remote_id = resource.organization_id + return if remote_id.blank? + organization_map[remote_id] + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/password.rb b/lib/sequencer/unit/import/zendesk/user/password.rb new file mode 100644 index 000000000..cb2b6a6ff --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/password.rb @@ -0,0 +1,26 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class Password < Sequencer::Unit::Common::Provider::Named + + uses :initiator + + private + + def password + # set the used import key as the admin password + # since we have no other confidential value + # that is known to Zammad and the User + return Setting.get('import_zendesk_endpoint_key') if initiator + # otherwise set an empty password so the user + # has to re-set a new password for Zammad + '' + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user/roles.rb b/lib/sequencer/unit/import/zendesk/user/roles.rb new file mode 100644 index 000000000..ed9cdb6db --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user/roles.rb @@ -0,0 +1,60 @@ +class Sequencer + class Unit + module Import + module Zendesk + module User + class Roles < Sequencer::Unit::Common::Provider::Named + + uses :resource, :initiator + + private + + def roles + return admin if initiator + map_roles + end + + def map_roles + return send(zendesk_role) if respond_to?(zendesk_role, true) + logger.error "Unknown mapping for role '#{resource.role.name}' (method: #{zendesk_role})" + end_user + end + + def zendesk_role + @zendesk_role ||= resource.role.name.tr('-', '_').to_sym + end + + def end_user + [role_customer] + end + + def agent + return [role_agent] if resource.restricted_agent + admin + end + + def admin + [role_admin, role_agent] + end + + def role_admin + @role_admin ||= lookup('Admin') + end + + def role_agent + @role_agent ||= lookup('Agent') + end + + def role_customer + @role_customer ||= lookup('Customer') + end + + def lookup(role_name) + ::Role.lookup(name: role_name) + end + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user_fields.rb b/lib/sequencer/unit/import/zendesk/user_fields.rb new file mode 100644 index 000000000..12992a039 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user_fields.rb @@ -0,0 +1,10 @@ +class Sequencer + class Unit + module Import + module Zendesk + class UserFields < Sequencer::Unit::Import::Zendesk::SubSequence::ObjectFields + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/user_group_map.rb b/lib/sequencer/unit/import/zendesk/user_group_map.rb new file mode 100644 index 000000000..d9a6bd19a --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/user_group_map.rb @@ -0,0 +1,28 @@ +class Sequencer + class Unit + module Import + module Zendesk + class UserGroupMap < Sequencer::Unit::Base + + uses :client + provides :user_group_map + + def process + state.provide(:user_group_map, mapping) + end + + private + + def mapping + result = {} + client.group_memberships.all! do |group_membership| + result[ group_membership.user_id ] ||= [] + result[ group_membership.user_id ].push( group_membership.group_id ) + end + result + end + end + end + end + end +end diff --git a/lib/sequencer/unit/import/zendesk/users.rb b/lib/sequencer/unit/import/zendesk/users.rb new file mode 100644 index 000000000..b89b5a9f1 --- /dev/null +++ b/lib/sequencer/unit/import/zendesk/users.rb @@ -0,0 +1,22 @@ +class Sequencer + class Unit + module Import + module Zendesk + class Users < Sequencer::Unit::Import::Zendesk::SubSequence::Object + + uses :organization_map, :group_map, :user_group_map + + private + + def default_params + super.merge( + organization_map: organization_map, + group_map: group_map, + user_group_map: user_group_map, + ) + end + end + end + end + end +end diff --git a/lib/sequencer/unit/zendesk/client.rb b/lib/sequencer/unit/zendesk/client.rb new file mode 100644 index 000000000..ae4054af2 --- /dev/null +++ b/lib/sequencer/unit/zendesk/client.rb @@ -0,0 +1,31 @@ +class Sequencer + class Unit + module Zendesk + class Client < Sequencer::Unit::Common::Provider::Fallback + + provides :client + + private + + def client + ZendeskAPI::Client.new do |config| + config.url = Setting.get('import_zendesk_endpoint') + + # Basic / Token Authentication + config.username = Setting.get('import_zendesk_endpoint_username') + config.token = Setting.get('import_zendesk_endpoint_key') + + # when hitting the rate limit, sleep automatically, + # then retry the request. + config.retry = true + + # disable cache to avoid unneeded memory consumption + # since we are using each object only once + # Inspired by: https://medium.com/swiftype-engineering/using-jmat-to-find-analyze-memory-in-jruby-1c4196c1ec72 + config.cache = false + end + end + end + end + end +end diff --git a/lib/sequencer/unit/zendesk/connected.rb b/lib/sequencer/unit/zendesk/connected.rb new file mode 100644 index 000000000..7149cc4b3 --- /dev/null +++ b/lib/sequencer/unit/zendesk/connected.rb @@ -0,0 +1,16 @@ +class Sequencer + class Unit + module Zendesk + class Connected < Sequencer::Unit::Common::Provider::Named + + uses :client + + private + + def connected + client.current_user.id.present? + end + end + end + end +end diff --git a/spec/lib/import/base_resource_spec.rb b/spec/lib/import/base_resource_spec.rb deleted file mode 100644 index d2392051c..000000000 --- a/spec/lib/import/base_resource_spec.rb +++ /dev/null @@ -1,149 +0,0 @@ -require 'rails_helper' - -RSpec.describe Import::BaseResource do - - it "needs an implementation of the 'import_class' method" do - expect do - described_class.new(attributes_for(:group)) - end.to raise_error(NoMethodError) - end - - context "implemented 'import_class' method" do - - before do - module Import - class Test < Import::Base - class Group < Import::BaseResource - - def import_class - ::Group - end - - def source - 'RSpec-TEST' - end - end - end - end - end - - after do - Import::Test.send(:remove_const, :Group) - end - - let(:attributes) do - attributes = attributes_for(:group) - attributes[:id] = 1337 - attributes - end - - context 'live run' do - - it 'creates new resources' do - expect do - Import::Test::Group.new(attributes) - end - .to change { - Group.count - }.by(1) - .and change { - ExternalSync.count - }.by(1) - end - - it 'updates existing resources' do - - # initial import - Import::Test::Group.new(attributes) - group = Group.last - - # simulate next import run - travel 20.minutes - - attributes[:note] = 'TEST' - - expect do - Import::Test::Group.new(attributes) - group.reload - end - .to change { - group.note - } - end - end - - context 'dry run' do - - it "doesn't create new resources" do - expect do - Import::Test::Group.new(attributes, dry_run: true) - end - .to not_change { - Group.count - } - .and not_change { - ExternalSync.count - } - end - - it "doesn't update existing resources" do - - # initial import - Import::Test::Group.new(attributes) - group = Group.last - - # simulate next import run - travel 20.minutes - - attributes[:note] = 'TEST' - - expect do - Import::Test::Group.new(attributes, dry_run: true) - group.reload - end - .to not_change { - group.note - } - .and not_change { - Group.count - } - .and not_change { - ExternalSync.count - } - end - - it "doesn't update associations of existing resources" do - - # initial import - Import::Test::Group.new(attributes) - group = Group.last - - old_signature = create(:signature) - old_users = create_list(:user, 2) - - group.update!(signature_id: old_signature.id) - group.update!(user_ids: old_users.collect(&:id)) - - # simulate next import run - travel 20.minutes - - new_signature = create(:signature) - new_users = create_list(:user, 2) - attributes[:signature_id] = new_signature.id - attributes[:user_ids] = new_users.collect(&:id) - - expect do - Import::Test::Group.new(attributes, dry_run: true) - group.reload - end - .to not_change { - group.signature_id - } - .and not_change { - group.user_ids - } - end - end - end - -end diff --git a/spec/lib/import/zendesk/base_factory_examples.rb b/spec/lib/import/zendesk/base_factory_examples.rb deleted file mode 100644 index bc38c2063..000000000 --- a/spec/lib/import/zendesk/base_factory_examples.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'lib/import/factory_examples' - -RSpec.shared_examples 'Import::Zendesk::BaseFactory' do - it_behaves_like 'Import::Factory' - - it 'calls .all! on parameter object' do - parameter = double() - expect(parameter).to receive('all!') - described_class.import(parameter) - end - - it 'calls new on determined backend object' do - expect(described_class).to receive(:backend_class).and_return(Class) - expect(described_class).to receive('skip?') - expect(described_class).to receive(:pre_import_hook) - expect(described_class).to receive(:post_import_hook) - record = double() - expect(Class).to receive(:new).with(record, any_args) - parameter = double() - expect(parameter).to receive('all!').and_yield(record) - described_class.import(parameter) - end -end diff --git a/spec/lib/import/zendesk/base_factory_spec.rb b/spec/lib/import/zendesk/base_factory_spec.rb deleted file mode 100644 index d0f00e68a..000000000 --- a/spec/lib/import/zendesk/base_factory_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/base_factory_examples' - -RSpec.describe Import::Zendesk::BaseFactory do - it_behaves_like 'Import::Zendesk::BaseFactory' -end diff --git a/spec/lib/import/zendesk/group_factory_spec.rb b/spec/lib/import/zendesk/group_factory_spec.rb deleted file mode 100644 index dd34d6174..000000000 --- a/spec/lib/import/zendesk/group_factory_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/base_factory_examples' -require 'lib/import/zendesk/local_id_mapper_hook_examples' - -RSpec.describe Import::Zendesk::GroupFactory do - it_behaves_like 'Import::Zendesk::BaseFactory' - it_behaves_like 'Import::Zendesk::LocalIDMapperHook' -end diff --git a/spec/lib/import/zendesk/group_spec.rb b/spec/lib/import/zendesk/group_spec.rb deleted file mode 100644 index 014f0e405..000000000 --- a/spec/lib/import/zendesk/group_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'rails_helper' - -RSpec.describe Import::Zendesk::Group do - - it 'creates a group if not exists' do - - group = double( - id: 31_337, - name: 'Test Group', - deleted: true - ) - - local_group = instance_double(::Group, id: 1337) - - expect(::Group).to receive(:create_if_not_exists).with(hash_including(name: group.name, active: !group.deleted)).and_return(local_group) - - created_instance = described_class.new(group) - - expect(created_instance).to respond_to(:id) - expect(created_instance.id).to eq(local_group.id) - - expect(created_instance).to respond_to(:zendesk_id) - expect(created_instance.zendesk_id).to eq(group.id) - end -end diff --git a/spec/lib/import/zendesk/local_id_mapper_hook_examples.rb b/spec/lib/import/zendesk/local_id_mapper_hook_examples.rb deleted file mode 100644 index 27dfddfe8..000000000 --- a/spec/lib/import/zendesk/local_id_mapper_hook_examples.rb +++ /dev/null @@ -1,20 +0,0 @@ -RSpec.shared_examples 'Import::Zendesk::LocalIDMapperHook' do - - it 'responds to local_id' do - expect(described_class).to respond_to('local_id') - end - - it 'responds to post_import_hook' do - expect(described_class).to respond_to('post_import_hook') - end - - it 'stores an ID mapping and makes it accessable' do - backend_instance = double( - zendesk_id: 31_337, - id: 1337, - ) - - described_class.post_import_hook(nil, backend_instance) - expect(described_class.local_id(backend_instance.zendesk_id)).to eq(backend_instance.id) - end -end diff --git a/spec/lib/import/zendesk/local_id_mapper_hook_spec.rb b/spec/lib/import/zendesk/local_id_mapper_hook_spec.rb deleted file mode 100644 index bf5c2ff46..000000000 --- a/spec/lib/import/zendesk/local_id_mapper_hook_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/local_id_mapper_hook_examples' - -RSpec.describe Import::Zendesk::LocalIDMapperHook do - it_behaves_like 'Import::Zendesk::LocalIDMapperHook' -end diff --git a/spec/lib/import/zendesk/lookup_backend_examples.rb b/spec/lib/import/zendesk/lookup_backend_examples.rb deleted file mode 100644 index f1e8220ff..000000000 --- a/spec/lib/import/zendesk/lookup_backend_examples.rb +++ /dev/null @@ -1,5 +0,0 @@ -RSpec.shared_examples 'Lookup backend' do - it 'responds to lookup' do - expect(described_class).to respond_to('lookup') - end -end diff --git a/spec/lib/import/zendesk/object_attribute_spec.rb b/spec/lib/import/zendesk/object_attribute/base_spec.rb similarity index 94% rename from spec/lib/import/zendesk/object_attribute_spec.rb rename to spec/lib/import/zendesk/object_attribute/base_spec.rb index 0539fa263..a253f0ce0 100644 --- a/spec/lib/import/zendesk/object_attribute_spec.rb +++ b/spec/lib/import/zendesk/object_attribute/base_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Import::Zendesk::ObjectAttribute do +RSpec.describe Import::Zendesk::ObjectAttribute::Base do it 'extends ObjectManager Attribute exception text' do diff --git a/spec/lib/import/zendesk/object_field_examples.rb b/spec/lib/import/zendesk/object_field_examples.rb deleted file mode 100644 index 6ad9993d6..000000000 --- a/spec/lib/import/zendesk/object_field_examples.rb +++ /dev/null @@ -1,22 +0,0 @@ -RSpec.shared_examples 'Import::Zendesk::ObjectField' do - - it 'initializes a object field backend import' do - - object_field = double(id: 31_337, title: 'Example Field') - allow(object_field).to receive(:[]).with('key').and_return(object_field.title) - - dummy_instance = double() - - local_name = 'example_field' - dummy_backend = instance_double(Class) - expect(dummy_backend).to receive(:new).with(kind_of(String), local_name, object_field).and_return(dummy_instance) - expect_any_instance_of(described_class).to receive(:backend_class).and_return(dummy_backend) - created_instance = described_class.new(object_field) - - expect(created_instance).to respond_to(:id) - expect(created_instance.id).to eq(local_name) - - expect(created_instance).to respond_to(:zendesk_id) - expect(created_instance.zendesk_id).to eq(object_field.id) - end -end diff --git a/spec/lib/import/zendesk/object_field_spec.rb b/spec/lib/import/zendesk/object_field_spec.rb deleted file mode 100644 index 035129ddb..000000000 --- a/spec/lib/import/zendesk/object_field_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/object_field_examples' - -RSpec.describe Import::Zendesk::ObjectField do - it_behaves_like 'Import::Zendesk::ObjectField' -end diff --git a/spec/lib/import/zendesk/organization_factory_spec.rb b/spec/lib/import/zendesk/organization_factory_spec.rb deleted file mode 100644 index 380ccb064..000000000 --- a/spec/lib/import/zendesk/organization_factory_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rails_helper' -require 'lib/import/factory_examples' -require 'lib/import/zendesk/local_id_mapper_hook_examples' - -RSpec.describe Import::Zendesk::OrganizationFieldFactory do - it_behaves_like 'Import::Factory' - it_behaves_like 'Import::Zendesk::LocalIDMapperHook' -end diff --git a/spec/lib/import/zendesk/organization_field_factory_spec.rb b/spec/lib/import/zendesk/organization_field_factory_spec.rb deleted file mode 100644 index d48fb5ccb..000000000 --- a/spec/lib/import/zendesk/organization_field_factory_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/base_factory_examples' -require 'lib/import/zendesk/local_id_mapper_hook_examples' - -RSpec.describe Import::Zendesk::OrganizationFieldFactory do - it_behaves_like 'Import::Zendesk::BaseFactory' - it_behaves_like 'Import::Zendesk::LocalIDMapperHook' -end diff --git a/spec/lib/import/zendesk/organization_field_spec.rb b/spec/lib/import/zendesk/organization_field_spec.rb deleted file mode 100644 index 01b6264ef..000000000 --- a/spec/lib/import/zendesk/organization_field_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/object_field_examples' - -RSpec.describe Import::Zendesk::OrganizationField do - it_behaves_like 'Import::Zendesk::ObjectField' -end diff --git a/spec/lib/import/zendesk/organization_spec.rb b/spec/lib/import/zendesk/organization_spec.rb deleted file mode 100644 index 24ba4f850..000000000 --- a/spec/lib/import/zendesk/organization_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'rails_helper' - -RSpec.describe Import::Zendesk::Organization do - - it 'creates a organization if not exists' do - - organization = double( - id: 31_337, - name: 'Test Organization', - note: 'Test Organization note', - shared_tickets: true, - organization_fields: nil - ) - - local_organization = instance_double(::Organization, id: 1337) - - expect(::Organization).to receive(:create_if_not_exists).with(hash_including(name: organization.name, note: organization.note, shared: organization.shared_tickets)).and_return(local_organization) - - created_instance = described_class.new(organization) - - expect(created_instance).to respond_to(:id) - expect(created_instance).to respond_to(:zendesk_id) - - expect(created_instance.id).to eq(local_organization.id) - expect(created_instance.zendesk_id).to eq(organization.id) - end -end diff --git a/spec/lib/import/zendesk/priority_spec.rb b/spec/lib/import/zendesk/priority_spec.rb deleted file mode 100644 index 711d8cc96..000000000 --- a/spec/lib/import/zendesk/priority_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/lookup_backend_examples' - -RSpec.describe Import::Zendesk::Priority do - it_behaves_like 'Lookup backend' - - it 'looks up ticket priority' do - - ticket = double(priority: nil) - dummy_result = 'dummy result' - expect(::Ticket::Priority).to receive(:lookup).and_return(dummy_result) - expect(described_class.lookup(ticket)).to eq(dummy_result) - end -end diff --git a/spec/lib/import/zendesk/state_spec.rb b/spec/lib/import/zendesk/state_spec.rb deleted file mode 100644 index efcb4622a..000000000 --- a/spec/lib/import/zendesk/state_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/lookup_backend_examples' - -RSpec.describe Import::Zendesk::State do - it_behaves_like 'Lookup backend' - - it 'looks up ticket state' do - - ticket = double(status: nil) - dummy_result = 'dummy result' - expect(::Ticket::State).to receive(:lookup).and_return(dummy_result) - expect(described_class.lookup(ticket)).to eq(dummy_result) - end -end diff --git a/spec/lib/import/zendesk/ticket/comment/attachment_factory_spec.rb b/spec/lib/import/zendesk/ticket/comment/attachment_factory_spec.rb deleted file mode 100644 index 706d6ba64..000000000 --- a/spec/lib/import/zendesk/ticket/comment/attachment_factory_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'rails_helper' -require 'lib/import/factory_examples' - -RSpec.describe Import::Zendesk::Ticket::Comment::AttachmentFactory do - it_behaves_like 'Import::Factory' - - it 'tunnels attachment and local article to backend' do - expect(described_class).to receive(:backend_class).and_return(Class) - expect(described_class).to receive('skip?') - expect(described_class).to receive(:pre_import_hook) - expect(described_class).to receive(:post_import_hook) - record = double() - local_article = double(attachments: []) - expect(Class).to receive(:new).with(record, local_article) - parameter = [record] - described_class.import(parameter, local_article) - end -end diff --git a/spec/lib/import/zendesk/ticket/comment/attachment_spec.rb b/spec/lib/import/zendesk/ticket/comment/attachment_spec.rb deleted file mode 100644 index 5376e8059..000000000 --- a/spec/lib/import/zendesk/ticket/comment/attachment_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'rails_helper' - -RSpec.describe Import::Zendesk::Ticket::Comment::Attachment do - - it 'downloads and stores attachments' do - - local_article = double(id: 1337) - - attachment = double( - file_name: 'Example.zip', - content_type: 'application/zip', - content_url: 'https://dl.remote.tld/394r0eifwskfjwlw3slf' - ) - - response = double( - body: 'content', - success?: true, - ) - - expect(UserAgent).to receive(:get).with(attachment.content_url, any_args).and_return(response) - - add_args = { - object: 'Ticket::Article', - o_id: local_article.id, - data: response.body, - filename: attachment.file_name, - preferences: { - 'Content-Type' => attachment.content_type - }, - created_by_id: 1 - } - - expect(Store).to receive(:add).with(add_args) - - described_class.new(attachment, local_article) - end -end diff --git a/spec/lib/import/zendesk/ticket/comment/local_id_lookup_backend_examples.rb b/spec/lib/import/zendesk/ticket/comment/local_id_lookup_backend_examples.rb deleted file mode 100644 index 254eaaa9b..000000000 --- a/spec/lib/import/zendesk/ticket/comment/local_id_lookup_backend_examples.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'rails_helper' - -RSpec.shared_examples 'local_id lookup backend' do - it 'responds to local_id' do - expect(described_class).to respond_to(:local_id) - end -end diff --git a/spec/lib/import/zendesk/ticket/comment/sender_spec.rb b/spec/lib/import/zendesk/ticket/comment/sender_spec.rb deleted file mode 100644 index af88a3380..000000000 --- a/spec/lib/import/zendesk/ticket/comment/sender_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/ticket/comment/local_id_lookup_backend_examples' - -RSpec.describe Import::Zendesk::Ticket::Comment::Sender do - it_behaves_like 'local_id lookup backend' -end diff --git a/spec/lib/import/zendesk/ticket/comment/type_spec.rb b/spec/lib/import/zendesk/ticket/comment/type_spec.rb deleted file mode 100644 index 526ceb8a6..000000000 --- a/spec/lib/import/zendesk/ticket/comment/type_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/ticket/comment/local_id_lookup_backend_examples' - -RSpec.describe Import::Zendesk::Ticket::Comment::Type do - it_behaves_like 'local_id lookup backend' -end diff --git a/spec/lib/import/zendesk/ticket/comment_factory_spec.rb b/spec/lib/import/zendesk/ticket/comment_factory_spec.rb deleted file mode 100644 index 13ab0aa82..000000000 --- a/spec/lib/import/zendesk/ticket/comment_factory_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/ticket/sub_object_factory_examples' - -RSpec.describe Import::Zendesk::Ticket::CommentFactory do - it_behaves_like 'Import::Zendesk::Ticket::SubObjectFactory' -end diff --git a/spec/lib/import/zendesk/ticket/comment_spec.rb b/spec/lib/import/zendesk/ticket/comment_spec.rb deleted file mode 100644 index 354f057d4..000000000 --- a/spec/lib/import/zendesk/ticket/comment_spec.rb +++ /dev/null @@ -1,637 +0,0 @@ -require 'rails_helper' - -RSpec.describe Import::Zendesk::Ticket::Comment do - - context 'email' do - - it 'creates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'email', - source: double( - from: double(address: 'from@sender.tld'), - to: double(address: 'to@receiver.tld') - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - create_structure = { - from: comment.via.source.from.address, - to: comment.via.source.to.address, - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 1, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(create_structure[:sender_id]) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id) - - expect(::Ticket::Article).to receive(:create).with(create_structure) - - described_class.new(comment, local_ticket, nil) - end - - it 'updates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'email', - source: double( - from: double(address: 'from@sender.tld'), - to: double(address: 'to@receiver.tld') - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - update_structure = { - from: comment.via.source.from.address, - to: comment.via.source.to.address, - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 1, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(update_structure[:sender_id]) - - local_article = double() - expect(local_article).to receive(:update!).with(update_structure) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id).and_return(local_article) - - described_class.new(comment, local_ticket, nil) - end - end - - context 'facebook' do - - context 'post' do - - it 'creates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'facebook', - source: double( - from: double(facebook_id: 3_129_033), - to: double(facebook_id: 1_230_920), - rel: 'post', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - create_structure = { - from: comment.via.source.from.facebook_id, - to: comment.via.source.to.facebook_id, - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 8, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(create_structure[:sender_id]) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id) - - expect(::Ticket::Article).to receive(:create).with(create_structure) - - described_class.new(comment, local_ticket, nil) - end - - it 'updates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'facebook', - source: double( - from: double(facebook_id: 3_129_033), - to: double(facebook_id: 1_230_920), - rel: 'post', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - update_structure = { - from: comment.via.source.from.facebook_id, - to: comment.via.source.to.facebook_id, - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 8, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(update_structure[:sender_id]) - - local_article = double() - expect(local_article).to receive(:update!).with(update_structure) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id).and_return(local_article) - - described_class.new(comment, local_ticket, nil) - end - end - - context 'comment' do - - it 'creates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'facebook', - source: double( - from: double(facebook_id: 3_129_033), - to: double(facebook_id: 1_230_920), - rel: 'comment', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - create_structure = { - from: comment.via.source.from.facebook_id, - to: comment.via.source.to.facebook_id, - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 9, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(create_structure[:sender_id]) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id) - - expect(::Ticket::Article).to receive(:create).with(create_structure) - - described_class.new(comment, local_ticket, nil) - end - - it 'updates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'facebook', - source: double( - from: double(facebook_id: 3_129_033), - to: double(facebook_id: 1_230_920), - rel: 'comment', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - update_structure = { - from: comment.via.source.from.facebook_id, - to: comment.via.source.to.facebook_id, - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 9, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(update_structure[:sender_id]) - - local_article = double() - expect(local_article).to receive(:update!).with(update_structure) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id).and_return(local_article) - - described_class.new(comment, local_ticket, nil) - end - end - end - - context 'twitter' do - - context 'mention' do - - it 'creates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'twitter', - source: double( - rel: 'mention', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - create_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 6, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(create_structure[:sender_id]) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id) - - expect(::Ticket::Article).to receive(:create).with(create_structure) - - described_class.new(comment, local_ticket, nil) - end - - it 'updates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'twitter', - source: double( - rel: 'mention', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - update_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 6, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(update_structure[:sender_id]) - - local_article = double() - expect(local_article).to receive(:update!).with(update_structure) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id).and_return(local_article) - - described_class.new(comment, local_ticket, nil) - end - end - - context 'direct_message' do - - it 'creates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'twitter', - source: double( - rel: 'direct_message', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - create_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 7, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(create_structure[:sender_id]) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id) - - expect(::Ticket::Article).to receive(:create).with(create_structure) - - described_class.new(comment, local_ticket, nil) - end - - it 'updates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'twitter', - source: double( - rel: 'direct_message', - ), - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - update_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 7, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(update_structure[:sender_id]) - - local_article = double() - expect(local_article).to receive(:update!).with(update_structure) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id).and_return(local_article) - - described_class.new(comment, local_ticket, nil) - end - end - end - - context 'web' do - - it 'creates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'web', - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - create_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 11, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(create_structure[:sender_id]) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id) - - expect(::Ticket::Article).to receive(:create).with(create_structure) - - described_class.new(comment, local_ticket, nil) - end - - it 'updates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'web', - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - update_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 11, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(update_structure[:sender_id]) - - local_article = double() - expect(local_article).to receive(:update!).with(update_structure) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id).and_return(local_article) - - described_class.new(comment, local_ticket, nil) - end - end - - context 'sample_ticket' do - - it 'creates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'sample_ticket', - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - create_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 10, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(create_structure[:sender_id]) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id) - - expect(::Ticket::Article).to receive(:create).with(create_structure) - - described_class.new(comment, local_ticket, nil) - end - - it 'updates' do - - comment = double( - id: 1337, - author_id: 42, - public: true, - html_body: '
Hello World!
', - via: double( - channel: 'sample_ticket', - ), - attachments: [] - ) - - local_ticket = double(id: 31_337) - - local_user_id = 99 - - update_structure = { - ticket_id: local_ticket.id, - body: comment.html_body, - content_type: 'text/html', - internal: !comment.public, - message_id: comment.id, - updated_by_id: local_user_id, - created_by_id: local_user_id, - sender_id: 23, - type_id: 10, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( comment.author_id ).and_return(local_user_id) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(update_structure[:sender_id]) - - local_article = double() - expect(local_article).to receive(:update!).with(update_structure) - - expect(::Ticket::Article).to receive(:find_by).with(message_id: comment.id).and_return(local_article) - - described_class.new(comment, local_ticket, nil) - end - end - -end diff --git a/spec/lib/import/zendesk/ticket/sub_object_factory_examples.rb b/spec/lib/import/zendesk/ticket/sub_object_factory_examples.rb deleted file mode 100644 index 89564b600..000000000 --- a/spec/lib/import/zendesk/ticket/sub_object_factory_examples.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'rails_helper' -require 'lib/import/factory_examples' - -RSpec.shared_examples 'Import::Zendesk::Ticket::SubObjectFactory' do - it_behaves_like 'Import::Factory' - - it 'tunnels local and remote ticket object to backend' do - - expect(described_class).to receive(:backend_class).and_return(Class) - expect(described_class).to receive('skip?') - expect(described_class).to receive(:pre_import_hook) - expect(described_class).to receive(:post_import_hook) - record = double() - local_ticket = double() - zendesk_ticket = double() - expect(Class).to receive(:new).with(record, local_ticket, zendesk_ticket) - parameter = double() - expect(parameter).to receive(:each).and_yield(record) - described_class.import(parameter, local_ticket, zendesk_ticket) - end -end diff --git a/spec/lib/import/zendesk/ticket/tag_factory_spec.rb b/spec/lib/import/zendesk/ticket/tag_factory_spec.rb deleted file mode 100644 index 9b893b229..000000000 --- a/spec/lib/import/zendesk/ticket/tag_factory_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/ticket/sub_object_factory_examples' - -RSpec.describe Import::Zendesk::Ticket::TagFactory do - it_behaves_like 'Import::Zendesk::Ticket::SubObjectFactory' -end diff --git a/spec/lib/import/zendesk/ticket/tag_spec.rb b/spec/lib/import/zendesk/ticket/tag_spec.rb deleted file mode 100644 index 62bad3e2c..000000000 --- a/spec/lib/import/zendesk/ticket/tag_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'rails_helper' - -# required due to some of rails autoloading issues -require 'import/zendesk/ticket/tag' - -RSpec.describe Import::Zendesk::Ticket::Tag do - - it 'creates ticket tags' do - - tag = double(id: 'Test Tag') - - local_ticket = instance_double(::Ticket, id: 1337) - - zendesk_ticket = double(requester_id: 31_337) - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with(zendesk_ticket.requester_id) - expect(::Tag).to receive(:tag_add).with(hash_including(item: tag.id, o_id: local_ticket.id)) - - described_class.new(tag, local_ticket, zendesk_ticket) - end -end diff --git a/spec/lib/import/zendesk/ticket_factory_spec.rb b/spec/lib/import/zendesk/ticket_factory_spec.rb deleted file mode 100644 index 6bfe6e105..000000000 --- a/spec/lib/import/zendesk/ticket_factory_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/base_factory_examples' - -RSpec.describe Import::Zendesk::TicketFactory do - it_behaves_like 'Import::Zendesk::BaseFactory' -end diff --git a/spec/lib/import/zendesk/ticket_field_factory_spec.rb b/spec/lib/import/zendesk/ticket_field_factory_spec.rb deleted file mode 100644 index f39a2df6e..000000000 --- a/spec/lib/import/zendesk/ticket_field_factory_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/base_factory_examples' -require 'lib/import/zendesk/local_id_mapper_hook_examples' - -RSpec.describe Import::Zendesk::TicketFieldFactory do - it_behaves_like 'Import::Zendesk::BaseFactory' - it_behaves_like 'Import::Zendesk::LocalIDMapperHook' -end diff --git a/spec/lib/import/zendesk/ticket_field_spec.rb b/spec/lib/import/zendesk/ticket_field_spec.rb deleted file mode 100644 index 327acc058..000000000 --- a/spec/lib/import/zendesk/ticket_field_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/object_field_examples' - -RSpec.describe Import::Zendesk::TicketField do - it_behaves_like 'Import::Zendesk::ObjectField' - - it 'handles fields with dashes in title' do - - zendesk_object = double( - id: 1337, - title: 'Priority - Simple', - key: 'priority_simple', - type: 'text', - removable: true, - active: true, - position: 1, - required_in_portal: true, - visible_in_portal: true, - required: true, - description: 'Example field', - ) - - expect(ObjectManager::Attribute).to receive(:migration_execute).and_return(true) - - expect do - described_class.new(zendesk_object) - end.not_to raise_error - - ObjectManager::Attribute.remove( - object: 'Ticket', - name: zendesk_object.key, - ) - end -end diff --git a/spec/lib/import/zendesk/ticket_spec.rb b/spec/lib/import/zendesk/ticket_spec.rb deleted file mode 100644 index 3ffaec04b..000000000 --- a/spec/lib/import/zendesk/ticket_spec.rb +++ /dev/null @@ -1,129 +0,0 @@ -require 'rails_helper' - -# required due to some of rails autoloading issues -require 'import/zendesk/ticket' - -RSpec.describe Import::Zendesk::Ticket do - - it 'creates' do - - ticket = double( - id: 1337, - subject: 'The ticket title', - description: 'An example ticket', - assignee: 69, - requester_id: 42, - group_id: 909, - organization_id: 101, - due_at: DateTime.tomorrow, - updated_at: DateTime.yesterday, - created_at: DateTime.yesterday, - tags: [], - comments: [], - custom_fields: [], - ) - - local_user_id = 23 - - expected_structure = { - id: ticket.id, - title: ticket.subject, - note: ticket.description, - group_id: 3, - owner_id: 101, - customer_id: local_user_id, - organization_id: 89, - state: 13, - priority: 7, - pending_time: ticket.due_at, - updated_at: ticket.updated_at, - created_at: ticket.created_at, - updated_by_id: local_user_id, - created_by_id: local_user_id, - create_article_sender_id: 21, - create_article_type_id: 555, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( ticket.requester_id ).and_return(local_user_id) - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( ticket.assignee ).and_return(expected_structure[:owner_id]) - - expect(Import::Zendesk::GroupFactory).to receive(:local_id).with( ticket.group_id ).and_return(expected_structure[:group_id]) - expect(Import::Zendesk::OrganizationFactory).to receive(:local_id).with( ticket.organization_id ).and_return(expected_structure[:organization_id]) - expect(Import::Zendesk::Priority).to receive(:lookup).with(ticket).and_return(expected_structure[:priority]) - expect(Import::Zendesk::State).to receive(:lookup).with(ticket).and_return(expected_structure[:state]) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(expected_structure[:create_article_sender_id]) - expect(Import::Zendesk::Ticket::Comment::Type).to receive(:local_id).with(ticket).and_return(expected_structure[:create_article_type_id]) - - local_ticket = double() - - expect(Import::Zendesk::Ticket::TagFactory).to receive(:import).with(ticket.tags, local_ticket, ticket) - expect(Import::Zendesk::Ticket::CommentFactory).to receive(:import).with(ticket.comments, local_ticket, ticket) - - expect(::Ticket).to receive(:find_by).with(id: expected_structure[:id]) - expect(::Ticket).to receive(:create).with(expected_structure).and_return(local_ticket) - - expect_any_instance_of(described_class).to receive(:reset_primary_key_sequence) - - created_instance = described_class.new(ticket) - end - - it 'updates' do - - ticket = double( - id: 1337, - subject: 'The ticket title', - description: 'An example ticket', - assignee: 69, - requester_id: 42, - group_id: 909, - organization_id: 101, - due_at: DateTime.tomorrow, - updated_at: DateTime.yesterday, - created_at: DateTime.yesterday, - tags: [], - comments: [], - custom_fields: [], - ) - - local_user_id = 23 - - expected_structure = { - id: ticket.id, - title: ticket.subject, - note: ticket.description, - group_id: 3, - owner_id: 101, - customer_id: local_user_id, - organization_id: 89, - state: 13, - priority: 7, - pending_time: ticket.due_at, - updated_at: ticket.updated_at, - created_at: ticket.created_at, - updated_by_id: local_user_id, - created_by_id: local_user_id, - create_article_sender_id: 21, - create_article_type_id: 555, - } - - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( ticket.requester_id ).and_return(local_user_id) - expect(Import::Zendesk::UserFactory).to receive(:local_id).with( ticket.assignee ).and_return(expected_structure[:owner_id]) - - expect(Import::Zendesk::GroupFactory).to receive(:local_id).with( ticket.group_id ).and_return(expected_structure[:group_id]) - expect(Import::Zendesk::OrganizationFactory).to receive(:local_id).with( ticket.organization_id ).and_return(expected_structure[:organization_id]) - expect(Import::Zendesk::Priority).to receive(:lookup).with(ticket).and_return(expected_structure[:priority]) - expect(Import::Zendesk::State).to receive(:lookup).with(ticket).and_return(expected_structure[:state]) - expect(Import::Zendesk::Ticket::Comment::Sender).to receive(:local_id).with(local_user_id).and_return(expected_structure[:create_article_sender_id]) - expect(Import::Zendesk::Ticket::Comment::Type).to receive(:local_id).with(ticket).and_return(expected_structure[:create_article_type_id]) - - local_ticket = double() - - expect(Import::Zendesk::Ticket::TagFactory).to receive(:import).with(ticket.tags, local_ticket, ticket) - expect(Import::Zendesk::Ticket::CommentFactory).to receive(:import).with(ticket.comments, local_ticket, ticket) - - expect(::Ticket).to receive(:find_by).with(id: expected_structure[:id]).and_return(local_ticket) - expect(local_ticket).to receive(:update!).with(expected_structure) - - created_instance = described_class.new(ticket) - end -end diff --git a/spec/lib/import/zendesk/user/group_spec.rb b/spec/lib/import/zendesk/user/group_spec.rb deleted file mode 100644 index 97ae249f8..000000000 --- a/spec/lib/import/zendesk/user/group_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/user/lookup_backend_examples' - -# required due to some of rails autoloading issues -require 'import/zendesk/user/group' - -RSpec.describe Import::Zendesk::User::Group do - it_behaves_like 'lookup backend' -end diff --git a/spec/lib/import/zendesk/user/lookup_backend_examples.rb b/spec/lib/import/zendesk/user/lookup_backend_examples.rb deleted file mode 100644 index 47ef2f94c..000000000 --- a/spec/lib/import/zendesk/user/lookup_backend_examples.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'rails_helper' - -RSpec.shared_examples 'lookup backend' do - it 'responds to for' do - expect(described_class).to respond_to(:for) - end -end diff --git a/spec/lib/import/zendesk/user/role_spec.rb b/spec/lib/import/zendesk/user/role_spec.rb deleted file mode 100644 index 9a0dff009..000000000 --- a/spec/lib/import/zendesk/user/role_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/user/lookup_backend_examples' - -# required due to some of rails autoloading issues -require 'import/zendesk/user/role' - -RSpec.describe Import::Zendesk::User::Role do - it_behaves_like 'lookup backend' - - it 'responds to map' do - expect(described_class).to respond_to(:map) - end -end diff --git a/spec/lib/import/zendesk/user_factory_spec.rb b/spec/lib/import/zendesk/user_factory_spec.rb deleted file mode 100644 index 294a3f656..000000000 --- a/spec/lib/import/zendesk/user_factory_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/base_factory_examples' -require 'lib/import/zendesk/local_id_mapper_hook_examples' - -RSpec.describe Import::Zendesk::UserFactory do - it_behaves_like 'Import::Zendesk::BaseFactory' - it_behaves_like 'Import::Zendesk::LocalIDMapperHook' -end diff --git a/spec/lib/import/zendesk/user_field_factory_spec.rb b/spec/lib/import/zendesk/user_field_factory_spec.rb deleted file mode 100644 index 601b0ab25..000000000 --- a/spec/lib/import/zendesk/user_field_factory_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/base_factory_examples' -require 'lib/import/zendesk/local_id_mapper_hook_examples' - -RSpec.describe Import::Zendesk::UserFieldFactory do - it_behaves_like 'Import::Zendesk::BaseFactory' - it_behaves_like 'Import::Zendesk::LocalIDMapperHook' -end diff --git a/spec/lib/import/zendesk/user_field_spec.rb b/spec/lib/import/zendesk/user_field_spec.rb deleted file mode 100644 index 2325622b7..000000000 --- a/spec/lib/import/zendesk/user_field_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'rails_helper' -require 'lib/import/zendesk/object_field_examples' - -RSpec.describe Import::Zendesk::UserField do - it_behaves_like 'Import::Zendesk::ObjectField' -end diff --git a/spec/lib/import/zendesk/user_spec.rb b/spec/lib/import/zendesk/user_spec.rb deleted file mode 100644 index 3c3d43eaa..000000000 --- a/spec/lib/import/zendesk/user_spec.rb +++ /dev/null @@ -1,165 +0,0 @@ -require 'rails_helper' - -# required due to some of rails autoloading issues -require 'import/zendesk/user' - -RSpec.describe Import::Zendesk::User do - - it 'create_or_updates user' do - - user = double( - id: 1337, - name: 'Example User', - email: 'user@example.com', - phone: '+49-123-345673', - suspended: false, - notes: 'Nice guy', - verified: true, - organization_id: 42, - last_login_at: DateTime.yesterday, - photo: double(content_url: 'https://img.remote.tld/w40293402zz394eed'), - user_fields: [], - ) - - expected_structure = { - login: user.email, - firstname: user.name, - email: user.email, - phone: user.phone, - password: '', - active: !user.suspended, - groups: [1, 2, 3], - roles: [3], - note: user.notes, - verified: user.verified, - organization_id: 101, - last_login: user.last_login_at, - image_source: user.photo.content_url, - updated_by_id: 1, - created_by_id: 1 - } - - local_user = double(id: 31_337) - - expect(Import::Zendesk::User::Group).to receive(:for).with(user).and_return(expected_structure[:groups]) - expect(Import::Zendesk::User::Role).to receive(:for).with(user).and_return(expected_structure[:roles]) - expect(Import::Zendesk::OrganizationFactory).to receive(:local_id).with( user.organization_id ).and_return(expected_structure[:organization_id]) - - expect(::User).to receive(:create_or_update).with( expected_structure ).and_return(local_user) - - created_instance = described_class.new(user) - - expect(created_instance).to respond_to(:id) - expect(created_instance.id).to eq(local_user.id) - - expect(created_instance).to respond_to(:zendesk_id) - expect(created_instance.zendesk_id).to eq(user.id) - end - - it 'imports id as login if no email address is available' do - - user = double( - id: 1337, - name: 'Example User', - email: nil, - phone: '+49-123-345673', - suspended: false, - notes: 'Nice guy', - verified: true, - organization_id: 42, - last_login_at: DateTime.yesterday, - photo: double(content_url: 'https://img.remote.tld/w40293402zz394eed'), - user_fields: [], - ) - - expected_structure = { - login: user.id.to_s, - firstname: user.name, - email: user.email, - phone: user.phone, - password: '', - active: !user.suspended, - groups: [1, 2, 3], - roles: [3], - note: user.notes, - verified: user.verified, - organization_id: 101, - last_login: user.last_login_at, - image_source: user.photo.content_url, - updated_by_id: 1, - created_by_id: 1 - } - - local_user = double(id: 31_337) - - expect(Import::Zendesk::User::Group).to receive(:for).with(user).and_return(expected_structure[:groups]) - expect(Import::Zendesk::User::Role).to receive(:for).with(user).and_return(expected_structure[:roles]) - expect(Import::Zendesk::OrganizationFactory).to receive(:local_id).with( user.organization_id ).and_return(expected_structure[:organization_id]) - - expect(::User).to receive(:create_or_update).with( expected_structure ).and_return(local_user) - - created_instance = described_class.new(user) - - expect(created_instance).to respond_to(:id) - expect(created_instance.id).to eq(local_user.id) - - expect(created_instance).to respond_to(:zendesk_id) - expect(created_instance.zendesk_id).to eq(user.id) - end - - it 'handles import user credentials and privileges specially' do - - user = double( - id: 1337, - name: 'Example User', - email: 'user@example.com', - phone: '+49-123-345673', - suspended: false, - notes: 'Nice guy', - verified: true, - organization_id: 42, - last_login_at: DateTime.yesterday, - photo: double(content_url: 'https://img.remote.tld/w40293402zz394eed'), - user_fields: [], - ) - - password = 'apikeyprovidedfortheimportbytheuser' - - expected_structure = { - login: user.email, - firstname: user.name, - email: user.email, - phone: user.phone, - password: password, - active: !user.suspended, - groups: [1, 2, 3], - roles: [1, 2], - note: user.notes, - verified: user.verified, - organization_id: 101, - last_login: user.last_login_at, - image_source: user.photo.content_url, - updated_by_id: 1, - created_by_id: 1 - } - - local_user = double(id: 31_337) - - expect(Import::Zendesk::User::Group).to receive(:for).with(user).and_return(expected_structure[:groups]) - expect(Import::Zendesk::User::Role).to receive(:map).with(user, 'admin').and_return(expected_structure[:roles]) - expect(Import::Zendesk::OrganizationFactory).to receive(:local_id).with( user.organization_id ).and_return(expected_structure[:organization_id]) - - expect(Setting).to receive(:get).with('import_zendesk_endpoint_username').twice.and_return(user.email) - expect(Setting).to receive(:get).with('import_zendesk_endpoint_key').and_return(password) - - expect(::User).to receive(:create_or_update).with( expected_structure ).and_return(local_user) - - created_instance = described_class.new(user) - - expect(created_instance).to respond_to(:id) - expect(created_instance.id).to eq(local_user.id) - - expect(created_instance).to respond_to(:zendesk_id) - expect(created_instance.zendesk_id).to eq(user.id) - end -end diff --git a/spec/lib/import/zendesk_spec.rb b/spec/lib/import/zendesk_spec.rb deleted file mode 100644 index 153fe393b..000000000 --- a/spec/lib/import/zendesk_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'rails_helper' -require 'lib/import/helper_examples' -require 'lib/import/importer_examples' -require 'lib/import/async_examples' -require 'lib/import/import_stats_examples' - -RSpec.describe Import::Zendesk do - it_behaves_like 'Import backend' - it_behaves_like 'Import::Helper' - it_behaves_like 'Import::Async' - it_behaves_like 'Import::ImportStats' -end diff --git a/spec/lib/sequencer/unit/import/common/object_attribute/sanitized_name_spec.rb b/spec/lib/sequencer/unit/import/common/object_attribute/sanitized_name_spec.rb new file mode 100644 index 000000000..ea4d43c40 --- /dev/null +++ b/spec/lib/sequencer/unit/import/common/object_attribute/sanitized_name_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +RSpec.describe Sequencer::Unit::Import::Common::ObjectAttribute::SanitizedName, sequencer: :unit do + + it 'requires implementation of .unsanitized_name' do + + expect do + process + end.to raise_error(RuntimeError) + end + + context 'sanitizes' do + + it 'whitespaces' do + provided = process do |instance| + expect(instance).to receive(:unsanitized_name).and_return('model name') + end + + expect(provided[:sanitized_name]).to eq('model_name') + end + + it 'dashes' do + provided = process do |instance| + expect(instance).to receive(:unsanitized_name).and_return('model-name') + end + + expect(provided[:sanitized_name]).to eq('model_name') + end + + it 'ids suffix' do + provided = process do |instance| + expect(instance).to receive(:unsanitized_name).and_return('Model Ids') + end + + expect(provided[:sanitized_name]).to eq('model_nos') + end + + it 'id suffix' do + provided = process do |instance| + expect(instance).to receive(:unsanitized_name).and_return('Model Id') + end + + expect(provided[:sanitized_name]).to eq('model_no') + end + end +end diff --git a/test/integration/zendesk_import_browser_test.rb b/test/integration/zendesk_import_browser_test.rb index 1963ac81d..da26c7f6d 100644 --- a/test/integration/zendesk_import_browser_test.rb +++ b/test/integration/zendesk_import_browser_test.rb @@ -79,32 +79,27 @@ class ZendeskImportBrowserTest < TestCase click(css: '.js-migration-start') watch_for( - css: '.js-group .js-done', + css: '.js-groups .js-done', value: '2', timeout: 120, ) watch_for( - css: '.js-organization .js-done', + css: '.js-organizations .js-done', value: '1', timeout: 120, ) watch_for( - css: '.js-user .js-done', + css: '.js-users .js-done', value: '141', timeout: 120, ) - watch_for( - css: '.js-ticket .js-done', - value: '143', - timeout: 800, - ) - watch_for( css: 'body', value: 'login', + timeout: 800, ) end end diff --git a/test/integration/zendesk_import_test.rb b/test/integration/zendesk_import_test.rb index fe598f861..1b781a9a1 100644 --- a/test/integration/zendesk_import_test.rb +++ b/test/integration/zendesk_import_test.rb @@ -20,29 +20,58 @@ class ZendeskImportTest < ActiveSupport::TestCase Setting.set('import_zendesk_endpoint_username', ENV['IMPORT_ZENDESK_ENDPOINT_USERNAME']) Setting.set('import_mode', true) Setting.set('system_init_done', false) - Import::Zendesk.start + + job = ImportJob.create(name: 'Import::Zendesk') + job.start # check statistic count test 'check statistic' do - remote_statistic = Import::Zendesk.statistic - # retrive statistic compare_statistic = { - 'Tickets' => 143, - 'TicketFields' => 13, - 'UserFields' => 2, - 'OrganizationFields' => 2, - 'Groups' => 2, - 'Organizations' => 1, - 'Users' => 141, - 'GroupMemberships' => 3, - 'Macros' => 5, - 'Views' => 19, - 'Automations' => 5 + Groups: { + skipped: 0, + created: 2, + updated: 0, + unchanged: 0, + failed: 0, + deactivated: 0, + sum: 2, + total: 2 + }, + Users: { + skipped: 0, + created: 141, + updated: 0, + unchanged: 0, + failed: 0, + deactivated: 0, + sum: 141, + total: 141 + }, + Organizations: { + skipped: 0, + created: 1, + updated: 0, + unchanged: 0, + failed: 0, + deactivated: 0, + sum: 1, + total: 1 + }, + Tickets: { + skipped: 0, + created: 142, + updated: 1, + unchanged: 0, + failed: 0, + deactivated: 0, + sum: 143, + total: 143 + } } - assert_equal(compare_statistic, remote_statistic, 'statistic') + assert_equal(compare_statistic.with_indifferent_access, job.result, 'statistic') end # check count of imported items