<%- @T('Enter your Email address and the Zendesk API token gained from your') %> <%- @T('admin interface') %>
-
+
+ <%- @T('Attention: These will be your login credentials after the import is completed.') %>
diff --git a/lib/import/base_factory.rb b/lib/import/base_factory.rb
index 550d5ebd6..0fe6746e6 100644
--- a/lib/import/base_factory.rb
+++ b/lib/import/base_factory.rb
@@ -4,13 +4,25 @@ module Import
# rubocop:disable Style/ModuleFunction
extend self
+ def import_action(records, *args)
+ pre_import_hook(records)
+ import_loop(records) do |record|
+ next if skip?(record)
+ backend_instance = create_instance(record, *args)
+ post_import_hook(record, backend_instance)
+ end
+ end
+
def import(_records)
- raise 'Missing implementation for import method for this factory'
+ raise 'Missing import method implementation for this factory'
end
def pre_import_hook(_records)
end
+ def post_import_hook(_record, _backend_instance)
+ end
+
def backend_class(_record)
"Import::#{module_name}".constantize
end
@@ -21,6 +33,14 @@ module Import
private
+ def create_instance(record, *args)
+ backend_class(record).new(record, *args)
+ end
+
+ def import_loop(records, &import_block)
+ records.each(&import_block)
+ end
+
def module_name
name.to_s.sub(/Import::/, '').sub(/Factory/, '')
end
diff --git a/lib/import/factory.rb b/lib/import/factory.rb
index baf6c35ed..1a5dc206b 100644
--- a/lib/import/factory.rb
+++ b/lib/import/factory.rb
@@ -4,13 +4,6 @@ module Import
# rubocop:disable Style/ModuleFunction
extend self
-
- def import(records)
- pre_import_hook(records)
- records.each do |record|
- next if skip?(record)
- backend_class(record).new(record)
- end
- end
+ alias import import_action
end
end
diff --git a/lib/import/transaction_factory.rb b/lib/import/transaction_factory.rb
index e35771a9a..8f1af13fb 100644
--- a/lib/import/transaction_factory.rb
+++ b/lib/import/transaction_factory.rb
@@ -7,11 +7,7 @@ module Import
def import(records)
ActiveRecord::Base.transaction do
- pre_import_hook(records)
- records.each do |record|
- next if skip?(record)
- backend_class(record).new(record)
- end
+ import_action(records)
end
end
end
diff --git a/lib/import/zendesk.rb b/lib/import/zendesk.rb
index 815df8146..598c3ceff 100644
--- a/lib/import/zendesk.rb
+++ b/lib/import/zendesk.rb
@@ -4,1067 +4,48 @@ require 'zendesk_api'
module Import
end
module Import::Zendesk
+ extend Import::Helper
+ extend Import::Zendesk::Async
+ extend Import::Zendesk::ImportStats
# rubocop:disable Style/ModuleFunction
extend self
def start
- Rails.logger.info 'Start import...'
+ log 'Start import...'
- # check if system is in import mode
- if !Setting.get('import_mode')
- raise 'System is not in import mode!'
- end
+ checks
- initialize_client
+ Import::Zendesk::GroupFactory.import(client.groups)
- import_fields
+ 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(client.tickets)
# TODO
- # import_oauth
- # import_twitter_channel
-
- import_groups
-
- import_organizations
-
- import_users
-
- import_tickets
-
- # TODO
- # import_sla_policies
-
- # import_macros
-
- # import_schedules
-
- # import_views
-
- # import_automations
-
Setting.set( 'system_init_done', true )
Setting.set( 'import_mode', false )
true
end
-=begin
- start import in background
-
- Import::Zendesk.start_bg
-=end
-
- 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 {
- loop do
- result = {
- data: current_state,
- result: 'in_progress',
- }
- Cache.write('import:state', result, expires_in: 10.minutes)
- sleep 8
- end
- }
- sleep 2
-
- # start import data
- begin
- Import::Zendesk.start
- rescue => e
- status_update_thread.exit
- status_update_thread.join
- Rails.logger.error e.message
- Rails.logger.error e.backtrace.inspect
- 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
-
-=begin
-
- get import state from background process
-
- result = Import::Zendesk.status_bg
-
-=end
-
- def status_bg
- state = Cache.get('import:state')
- return state if state
- {
- message: 'not running',
- }
- end
-
-=begin
-
- start get request to backend to check connection
-
- result = connection_test
-
- return
-
- true | false
-
-=end
-
def connection_test
- initialize_client
-
- return true if @client.users.first
- false
+ Import::Zendesk::Requester.connection_test
end
- def statistic
+ private
- # check cache
- cache = Cache.get('import_zendesk_stats')
- if cache
- return cache
- end
-
- initialize_client
-
- # 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 { |object, _score|
- result[ object ] = @client.send( object.underscore.to_sym ).count!
- }
-
- if result
- Cache.write('import_zendesk_stats', result)
- end
- result
+ def client
+ Import::Zendesk::Requester.client
end
-=begin
-
- return current import state
-
- result = current_state
-
- return
-
- {
- Group: {
- total: 1234,
- done: 13,
- },
- Organization: {
- total: 1234,
- done: 13,
- },
- User: {
- total: 1234,
- done: 13,
- },
- Ticket: {
- total: 1234,
- done: 13,
- },
- }
-
-=end
-
- 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 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
- end
- end
-
- def mapping_state(zendesk_state)
-
- mapping = {
- 'pending' => 'pending reminder',
- 'solved' => 'closed',
- }
- return zendesk_state if !mapping[zendesk_state]
- mapping[zendesk_state]
- end
-
- def mapping_priority(zendesk_priority)
-
- mapping = {
- 'low' => '1 low',
- nil => '2 normal',
- 'normal' => '2 normal',
- 'high' => '3 high',
- 'urgent' => '3 high',
- }
- mapping[zendesk_priority]
- end
-
- # NOT IMPLEMENTED YET
- def mapping_type(zendesk_type)
-
- mapping = {
- nil => '',
- 'question' => '',
- 'incident' => '',
- 'problem' => '',
- 'task' => '',
- }
- return zendesk_type if !mapping[zendesk_type]
- mapping[zendesk_type]
- end
-
- def mapping_ticket_field(zendesk_field)
-
- mapping = {
- 'subject' => 'title',
- 'description' => 'note',
- 'status' => 'state_id',
- 'tickettype' => 'type',
- 'priority' => 'priority_id',
- 'group' => 'group_id',
- 'assignee' => 'owner_id',
- }
- return zendesk_field if !mapping[zendesk_field]
- mapping[zendesk_field]
- end
-
- # FILTER:
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/views#conditions-reference
- def mapping_filter(zendesk_filter)
-
- end
-
- # Ticket Fields
- # User Fields
- # Organization Fields
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/ticket_fields
- # https://developer.zendesk.com/rest_api/docs/core/user_fields
- # https://developer.zendesk.com/rest_api/docs/core/organization_fields
- def import_fields
-
- %w(Ticket User Organization).each { |local_object|
-
- local_fields = local_object.constantize.column_names
-
- @client.send("#{local_object.downcase}_fields").all! { |zendesk_object_field|
-
- if local_object == 'Ticket'
- mapped_object_field = method("mapping_#{local_object.downcase}_field").call( zendesk_object_field.type )
-
- next if local_fields.include?( mapped_object_field )
- end
-
- import_field(local_object, zendesk_object_field)
- }
- }
- end
-
- def import_field(local_object, zendesk_field)
-
- name = ''
- name = if local_object == 'Ticket'
- zendesk_field.title
- else
- zendesk_field['key'] # TODO: y?!
- end
-
- @zendesk_field_mapping ||= {}
- @zendesk_field_mapping[ zendesk_field.id ] = name
-
- data_type = zendesk_field.type
- data_option = {
- null: !zendesk_field.required,
- note: zendesk_field.description,
- }
-
- if zendesk_field.type == 'date'
- data_option = {
- future: true,
- past: true,
- diff: 0,
- }.merge(data_option)
- elsif zendesk_field.type == 'checkbox'
- data_type = 'boolean'
- data_option = {
- default: false,
- options: {
- true => 'yes',
- false => 'no',
- },
- }.merge(data_option)
- elsif zendesk_field.type == 'regexp'
- data_type = 'input'
- data_option = {
- type: 'text',
- maxlength: 255,
- regex: zendesk_field.regexp_for_validation,
- }.merge(data_option)
- elsif zendesk_field.type == 'decimal'
- data_type = 'input'
- data_option = {
- type: 'text',
- maxlength: 255,
- }.merge(data_option)
- elsif zendesk_field.type == 'integer'
- data_type = 'integer'
- data_option = {
- min: 0,
- max: 999_999_999,
- }.merge(data_option)
- elsif zendesk_field.type == 'text'
- data_type = 'input'
- data_option = {
- type: zendesk_field.type,
- maxlength: 255,
- }.merge(data_option)
- elsif zendesk_field.type == 'textarea'
- data_type = 'input'
- data_option = {
- type: zendesk_field.type,
- maxlength: 255,
- }.merge(data_option)
- elsif zendesk_field.type == 'tagger' || zendesk_field.type == 'dropdown'
-
- # \"custom_field_options\"=>[{\"id\"=>28353445
- # \"name\"=>\"Another Value\"
- # \"raw_name\"=>\"Another Value\"
- # \"value\"=>\"anotherkey\"}
- # {\"id\"=>28353425
- # \"name\"=>\"Value 1\"
- # \"raw_name\"=>\"Value 1\"
- # \"value\"=>\"key1\"}
- # {\"id\"=>28353435
- # \"name\"=>\"Value 2\"
- # \"raw_name\"=>\"Value 2\"
- # \"value\"=>\"key2\"}]}>
- # "
-
- options = {}
- @zendesk_ticket_field_value_mapping ||= {}
- zendesk_field.custom_field_options.each { |entry|
-
- if local_object == 'Ticket'
- @zendesk_ticket_field_value_mapping[ name ] ||= {}
- @zendesk_ticket_field_value_mapping[ name ][ entry['id'] ] = entry['value']
- end
-
- options[ entry['value'] ] = entry['name']
- }
-
- data_type = 'select'
- data_option = {
- default: '',
- options: options,
- }.merge(data_option)
- end
-
- screens = {
- view: {
- '-all-' => {
- shown: true,
- },
- }
- }
-
- if zendesk_field.visible_in_portal || !zendesk_field.required_in_portal
- screens = {
- edit: {
- Customer: {
- shown: zendesk_field.visible_in_portal,
- null: !zendesk_field.required_in_portal,
- },
- }.merge(screens)
- }
- end
- name.gsub!(/\s/, '_')
-
- ObjectManager::Attribute.add(
- object: local_object,
- name: name,
- display: zendesk_field.title,
- data_type: data_type,
- data_option: data_option,
- editable: !zendesk_field.removable,
- active: zendesk_field.active,
- screens: screens,
- position: zendesk_field.position,
- created_by_id: 1,
- updated_by_id: 1,
- )
- ObjectManager::Attribute.migration_execute(false)
- end
-
- # OAuth
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/oauth_tokens
- # https://developer.zendesk.com/rest_api/docs/core/oauth_clients
- def import_oauth
-
- end
-
- # Twitter
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/twitter_channel
- def import_twitter
-
- end
-
- # Groups
- # https://developer.zendesk.com/rest_api/docs/core/groups
- def import_groups
-
- @zendesk_group_mapping = {}
- @client.groups.all! { |zendesk_group|
- local_group = Group.create_if_not_exists(
- name: zendesk_group.name,
- active: !zendesk_group.deleted,
- updated_by_id: 1,
- created_by_id: 1
- )
-
- @zendesk_group_mapping[ zendesk_group.id ] = local_group.id
- }
- end
-
- # Organizations
- # https://developer.zendesk.com/rest_api/docs/core/organizations
- def import_organizations
-
- @zendesk_organization_mapping = {}
-
- @client.organizations.each { |zendesk_organization|
- custom_fields = get_fields(zendesk_organization.organization_fields)
-
- local_organization_fields = {
- name: zendesk_organization.name,
- note: zendesk_organization.note,
- shared: zendesk_organization.shared_tickets,
- # shared: zendesk_organization.shared_comments, # TODO, not yet implemented
- # }.merge(zendesk_organization.organization_fields) # TODO
- updated_by_id: 1,
- created_by_id: 1
- }.merge(custom_fields)
-
- local_organization = Organization.create_if_not_exists(local_organization_fields)
- @zendesk_organization_mapping[ zendesk_organization.id ] = local_organization.id
- }
- end
-
- # Users
- # https://developer.zendesk.com/rest_api/docs/core/users
- def import_users
- import_group_memberships
- import_custom_roles
-
- @zendesk_user_mapping = {}
-
- role_admin = Role.lookup(name: 'Admin')
- role_agent = Role.lookup(name: 'Agent')
- role_customer = Role.lookup(name: 'Customer')
-
- @client.users.all! { |zendesk_user|
- custom_fields = get_fields(zendesk_user.user_fields)
- local_user_fields = {
- login: zendesk_user.id.to_s, # Zendesk users may have no other identifier than the ID, e.g. twitter users
- firstname: zendesk_user.name,
- email: zendesk_user.email,
- phone: zendesk_user.phone,
- password: '',
- active: !zendesk_user.suspended,
- groups: [],
- roles: [],
- note: zendesk_user.notes,
- verified: zendesk_user.verified,
- organization_id: @zendesk_organization_mapping[ zendesk_user.organization_id ],
- last_login: zendesk_user.last_login_at,
- updated_by_id: 1,
- created_by_id: 1
- }.merge(custom_fields)
-
- if @zendesk_user_group_mapping[ zendesk_user.id ]
-
- @zendesk_user_group_mapping[ zendesk_user.id ].each { |zendesk_group_id|
-
- local_group_id = @zendesk_group_mapping[ zendesk_group_id ]
-
- next if !local_group_id
-
- group = Group.find( local_group_id )
-
- local_user_fields[:groups].push group
- }
- end
-
- if zendesk_user.role.name == 'end-user'
- local_user_fields[:roles].push role_customer
-
- elsif zendesk_user.role.name == 'agent'
-
- local_user_fields[:roles].push role_agent
-
- if !zendesk_user.restricted_agent
- local_user_fields[:roles].push role_admin
- end
-
- elsif zendesk_user.role.name == 'admin'
- local_user_fields[:roles].push role_agent
- local_user_fields[:roles].push role_admin
- end
-
- if zendesk_user.photo && zendesk_user.photo.content_url
- local_user_fields[:image_source] = zendesk_user.photo.content_url
- end
-
- # TODO
- # local_user_fields = local_user_fields.merge( user.user_fields )
-
- # TODO
- # user.custom_role_id (Enterprise only)
- local_user = User.create_or_update( local_user_fields )
-
- @zendesk_user_mapping[ zendesk_user.id ] = local_user.id
- }
- end
-
- # Group Memberships
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/group_memberships
- def import_group_memberships
-
- @zendesk_user_group_mapping = {}
-
- @client.group_memberships.all! { |zendesk_group_membership|
-
- @zendesk_user_group_mapping[ zendesk_group_membership.user_id ] ||= []
- @zendesk_user_group_mapping[ zendesk_group_membership.user_id ].push( zendesk_group_membership.group_id )
- }
- end
-
- # Custom Roles (Enterprise only)
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/custom_roles
- def import_custom_roles
-
- end
-
- # Tickets
- # 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
- def import_tickets
-
- article_sender_customer = Ticket::Article::Sender.lookup(name: 'Customer')
- article_sender_agent = Ticket::Article::Sender.lookup(name: 'Agent')
- article_sender_system = Ticket::Article::Sender.lookup(name: 'System')
-
- initialize_article_types
-
- @client.tickets.all! { |zendesk_ticket|
- custom_fields = get_custom_fields(zendesk_ticket.custom_fields)
- local_ticket_fields = {
- id: zendesk_ticket.id,
- title: zendesk_ticket.subject,
- note: zendesk_ticket.description,
- group_id: @zendesk_group_mapping[ zendesk_ticket.group_id ] || 1,
- customer_id: @zendesk_user_mapping[ zendesk_ticket.requester_id ] || 1,
- organization_id: @zendesk_organization_mapping[ zendesk_ticket.organization_id ],
- state: Ticket::State.lookup( name: mapping_state( zendesk_ticket.status ) ),
- priority: Ticket::Priority.lookup( name: mapping_priority( zendesk_ticket.priority ) ),
- pending_time: zendesk_ticket.due_at,
- updated_at: zendesk_ticket.updated_at,
- created_at: zendesk_ticket.created_at,
- updated_by_id: @zendesk_user_mapping[ zendesk_ticket.requester_id ] || 1,
- created_by_id: @zendesk_user_mapping[ zendesk_ticket.requester_id ] || 1,
- }.merge(custom_fields)
- ticket_author = User.find( @zendesk_user_mapping[ zendesk_ticket.requester_id ] || 1 )
-
- local_ticket_fields[:create_article_sender_id] = if ticket_author.role?('Customer')
- article_sender_customer.id
- elsif ticket_author.role?('Agent')
- article_sender_agent.id
- else
- article_sender_system.id
- end
-
- local_ticket_fields[:create_article_type_id] = article_type_lookup(zendesk_ticket.via)
-
- local_ticket = Ticket.find_by(id: local_ticket_fields[:id])
- if local_ticket
- local_ticket.update_attributes(local_ticket_fields)
- else
- local_ticket = Ticket.create(local_ticket_fields)
- _reset_pk('tickets')
- end
-
- zendesk_ticket_tags = []
- zendesk_ticket.tags.each { |tag|
- zendesk_ticket_tags.push(tag)
- }
-
- zendesk_ticket_tags.each { |tag|
- Tag.tag_add(
- object: 'Ticket',
- o_id: local_ticket.id,
- item: tag.id,
- created_by_id: @zendesk_user_mapping[ zendesk_ticket.requester_id ] || 1,
- )
- }
-
- zendesk_ticket_articles = []
- zendesk_ticket.comments.each { |zendesk_article|
- zendesk_ticket_articles.push(zendesk_article)
- }
-
- zendesk_ticket_articles.each { |zendesk_article|
-
- local_article_fields = {
- ticket_id: local_ticket.id,
- body: zendesk_article.html_body,
- internal: !zendesk_article.public,
- message_id: zendesk_article.id,
- updated_by_id: @zendesk_user_mapping[ zendesk_article.author_id ] || 1,
- created_by_id: @zendesk_user_mapping[ zendesk_article.author_id ] || 1,
- }
-
- article_author = User.find( @zendesk_user_mapping[ zendesk_article.author_id ] || 1 )
-
- local_article_fields[:sender_id] = if article_author.role?('Customer')
- article_sender_customer.id
- elsif article_author.role?('Agent')
- article_sender_agent.id
- else
- article_sender_system.id
- end
-
- local_article_fields[:type_id] = article_type_lookup(zendesk_article.via)
-
- if zendesk_article.via.channel == 'email'
- local_article_fields[:from] = zendesk_article.via.source.from.address
- local_article_fields[:to] = zendesk_article.via.source.to.address # Notice zendesk_article.via.from.original_recipients=[\"another@gmail.com\", \"support@example.zendesk.com\"]
- elsif zendesk_article.via.channel == 'facebook'
- local_article_fields[:from] = zendesk_article.via.source.from.facebook_id
- local_article_fields[:to] = zendesk_article.via.source.to.facebook_id
- end
-
- # create article
- local_article = Ticket::Article.find_by(message_id: local_article_fields[:message_id])
- if local_article
- local_article.update_attributes(local_article_fields)
- else
- local_article = Ticket::Article.create( local_article_fields )
- end
-
- zendesk_attachments = zendesk_article.attachments
-
- next if zendesk_attachments.size.zero?
-
- local_attachments = local_article.attachments
-
- zendesk_ticket_attachments = []
- zendesk_attachments.each { |zendesk_attachment|
- zendesk_ticket_attachments.push(zendesk_attachment)
- }
-
- zendesk_ticket_attachments.each { |zendesk_attachment|
-
- response = UserAgent.get(
- zendesk_attachment.content_url,
- {},
- {
- open_timeout: 10,
- read_timeout: 60,
- },
- )
-
- if !response.success?
- Rails.logger.error response.error
- next
- end
-
- local_attachment = Store.add(
- object: 'Ticket::Article',
- o_id: local_article.id,
- data: response.body,
- filename: zendesk_attachment.file_name,
- preferences: {
- 'Content-Type' => zendesk_attachment.content_type
- },
- created_by_id: 1
- )
- }
- }
- }
- end
-
- # SLA Policies
- # TODO:
- # https://github.com/zendesk/zendesk_api_client_rb/issues/271
- # https://developer.zendesk.com/rest_api/docs/core/sla_policies
- def import_sla_policies
-
- end
-
- # Macros
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/macros
- def import_macros
-
- @client.macros.all! { |zendesk_macro|
-
- # TODO
- next if !zendesk_macro.active
-
- # "url"=>"https://example.zendesk.com/api/v2/macros/59511191.json"
- # "id"=>59511191
- # "title"=>"Herabstufen und informieren"
- # "active"=>true
- # "updated_at"=>2015-08-03 13:51:14 UTC
- # "created_at"=>2015-07-19 22:41:42 UTC
- # "restriction"=>nil
- # "actions"=>[
- # {
- # "field"=>"priority"
- # "value"=>"low"
- # }
- # {
- # "field"=>"comment_value"
- # "value"=>"Das Verkehrsaufkommen ist g....."
- # }
- # ]
-
- perform = {}
- zendesk_macro.actions.each { |action|
-
- # TODO: ID fields
- perform["ticket.#{action.field}"] = action.value
- }
-
- Macro.create_if_not_exists(
- name: zendesk_macro.title,
- perform: perform,
- note: '',
- active: zendesk_macro.active,
- )
- }
- end
-
- # Schedulers
- # TODO:
- # https://github.com/zendesk/zendesk_api_client_rb/issues/281
- # https://developer.zendesk.com/rest_api/docs/core/schedules
- def import_schedules
-
- end
-
- # Views
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/views
- def import_views
-
- @client.views.all! { |zendesk_view|
-
- # "url" => "https://example.zendesk.com/api/v2/views/59511071.json"
- # "id" => 59511071
- # "title" => "Ihre Tickets"
- # "active" => true
- # "updated_at" => 2015-08-03 13:51:14 UTC
- # "created_at" => 2015-07-19 22:41:42 UTC
- # "restriction" => nil
- # "sla_id" => nil
- # "execution" => {
- # "group_by" => "status"
- # "group_order" => "asc"
- # "sort_by" => "score"
- # "sort_order" => "desc"
- # "group" => {
- # "id" => "status"
- # "title" => "Status"
- # "order" => "asc"
- # }
- # "sort" => {
- # "id" => "score"
- # "title" => "Score"
- # "order" => "desc"
- # }
- # "columns" => [
- # {
- # "id" => "score"
- # "title" => "Score"
- # }
- # {
- # "id" => "subject"
- # "title" => "Subject"
- # }
- # {
- # "id" => "requester"
- # "title" => "Requester"
- # }
- # {
- # "id" => "created"
- # "title" => "Requested"
- # }
- # {
- # "id" => "type"
- # "title" => "Type"
- # }
- # {
- # "id" => "priority"
- # "title" => "Priority"
- # }
- # ]
- # "fields" => [
- # {
- # "id" => "score"
- # "title" => "Score"
- # }
- # {
- # "id" => "subject"
- # "title" => "Subject"
- # }
- # {
- # "id" => "requester"
- # "title" => "Requester"
- # }
- # {
- # "id" => "created"
- # "title" => "Requested"
- # }
- # {
- # "id" => "type"
- # "title" => "Type"
- # }
- # {
- # "id" => "priority"
- # "title" => "Priority"
- # }
- # ]
- # "custom_fields" => []
- # }
- # "conditions" => {
- # "all" => [
- # {
- # "field" => "status"
- # "operator" => "less_than"
- # "value" => "solved"
- # }
- # {
- # "field" => "assignee_id"
- # "operator" => "is"
- # "value" => "current_user"
- # }
- # ]
- # "any" => []
- # }
-
- Overview.create_if_not_exists(
- name: zendesk_view.title,
- link: 'my_assigned', # TODO
- prio: 1000,
- role_id: overview_role.id,
- condition: {
- 'ticket.state_id' => {
- operator: 'is',
- value: [ 1, 2, 3, 7 ],
- },
- 'ticket.owner_id' => {
- operator: 'is',
- pre_condition: 'current_user.id',
- },
- },
- order: {
- by: 'created_at',
- direction: 'ASC',
- },
- view: {
- d: %w(title customer group created_at),
- s: %w(title customer group created_at),
- m: %w(number title customer group created_at),
- view_mode_default: 's',
- },
- )
- }
- end
-
- # Automations
- # TODO:
- # https://developer.zendesk.com/rest_api/docs/core/automations
- def import_automations
-
- @client.automations.all! { |_zendesk_automation|
-
- # "url" => "https://example.zendesk.com/api/v2/automations/60037892.json"
- # "id" => 60037892
- # "title" => "Ticket aus Facebook-Nachricht 1 ..."
- # "active" => true
- # "updated_at" => 2015-08-03 13:51:15 UTC
- # "created_at" => 2015-07-28 11:27:50 UTC
- # "actions" => [
- # {
- # "field" => "status"
- # "value" => "closed"
- # }
- # ]
- # "conditions" => {
- # "all" => [
- # {
- # "field" => "status"
- # "operator" => "is"
- # "value" => "solved"
- # }
- # {
- # "field" => "SOLVED"
- # "operator" => "is"
- # "value" => "24"
- # }
- # {
- # "field" => "via_type"
- # "operator" => "is"
- # "value" => "facebook"
- # }
- # ]
- # "any" => []
- # }
- # "position" => 10000
-
- }
- end
-
- # reset primary key sequences
- def self._reset_pk(table)
- DbHelper.import_post(table)
- end
-
- def get_custom_fields(custom_fields)
- return {} if !custom_fields
- fields = {}
- custom_fields.each { |custom_field|
- field_name = @zendesk_field_mapping[ custom_field['id'] ].gsub(/\s/, '_')
- field_value = custom_field['value']
- next if field_value.nil? # ignore nil values
- if @zendesk_ticket_field_value_mapping[ field_name ]
- field_value = @zendesk_ticket_field_value_mapping[ field_name ][ field_value ]
- end
- fields[ field_name.to_sym ] = field_value
- }
- fields
- end
-
- def get_fields(user_fields)
- return {} if !user_fields
- fields = {}
- user_fields.each { |key, value|
- fields[key] = value
- }
- fields
- end
-
- def initialize_article_types
- 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
- end
-
- def article_type_lookup(via)
- case via.channel
- when 'web'
- @article_type_id[:web]
- when 'email'
- @article_type_id[:email]
- when 'sample_ticket'
- @article_type_id[:note]
- when 'twitter'
- if via.source.rel == 'mention'
- @article_type_id[:twitter_status]
- else
- @article_type_id[:twitter_direct_message]
- end
- when 'facebook'
- if 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
+ def checks
+ check_import_mode
+ connection_test
end
end
diff --git a/lib/import/zendesk/async.rb b/lib/import/zendesk/async.rb
new file mode 100644
index 000000000..31df71b68
--- /dev/null
+++ b/lib/import/zendesk/async.rb
@@ -0,0 +1,65 @@
+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 {
+ loop do
+ result = {
+ data: current_state,
+ result: 'in_progress',
+ }
+ Cache.write('import:state', result, expires_in: 10.minutes)
+ sleep 8
+ end
+ }
+ sleep 2
+
+ # start import data
+ begin
+ Import::Zendesk.start
+ rescue => e
+ status_update_thread.exit
+ status_update_thread.join
+ Rails.logger.error e.message
+ Rails.logger.error e.backtrace.inspect
+ 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
new file mode 100644
index 000000000..5ec8fdfdb
--- /dev/null
+++ b/lib/import/zendesk/base_factory.rb
@@ -0,0 +1,16 @@
+module Import
+ module Zendesk
+ module BaseFactory
+ include Import::Factory
+
+ # rubocop:disable Style/ModuleFunction
+ extend self
+
+ private
+
+ def import_loop(records, &import_block)
+ records.all!(&import_block)
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/group.rb b/lib/import/zendesk/group.rb
new file mode 100644
index 000000000..0c0c14566
--- /dev/null
+++ b/lib/import/zendesk/group.rb
@@ -0,0 +1,21 @@
+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
new file mode 100644
index 000000000..3386da928
--- /dev/null
+++ b/lib/import/zendesk/group_factory.rb
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 000000000..bdd210f57
--- /dev/null
+++ b/lib/import/zendesk/helper.rb
@@ -0,0 +1,19 @@
+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 { |key, value|
+ fields[key] = value
+ }
+ fields
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/import_stats.rb b/lib/import/zendesk/import_stats.rb
new file mode 100644
index 000000000..c49a8f0df
--- /dev/null
+++ b/lib/import/zendesk/import_stats.rb
@@ -0,0 +1,61 @@
+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 { |object, _score|
+ result[ object ] = Import::Zendesk::Requester.client.send( object.underscore.to_sym ).count!
+ }
+
+ Cache.write('import_zendesk_stats', result)
+ result
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/local_id_mapper_hook.rb b/lib/import/zendesk/local_id_mapper_hook.rb
new file mode 100644
index 000000000..b300caa9a
--- /dev/null
+++ b/lib/import/zendesk/local_id_mapper_hook.rb
@@ -0,0 +1,25 @@
+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
new file mode 100644
index 000000000..4c8824487
--- /dev/null
+++ b/lib/import/zendesk/object_attribute.rb
@@ -0,0 +1,73 @@
+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)
+ raise 'Missing init_callback method implementation for this object attribute'
+ end
+
+ def add(object, name, attribute)
+ ObjectManager::Attribute.add( attribute_config(object, name, attribute) )
+ ObjectManager::Attribute.migration_execute(false)
+ 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/checkbox.rb b/lib/import/zendesk/object_attribute/checkbox.rb
new file mode 100644
index 000000000..143b629e6
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/checkbox.rb
@@ -0,0 +1,23 @@
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Checkbox < Import::Zendesk::ObjectAttribute
+ def init_callback(_object_attribte)
+ @data_option.merge!(
+ default: false,
+ options: {
+ true => 'yes',
+ false => 'no',
+ },
+ )
+ end
+
+ private
+
+ def data_type(_attribute)
+ 'boolean'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/date.rb b/lib/import/zendesk/object_attribute/date.rb
new file mode 100644
index 000000000..ed7ba4165
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/date.rb
@@ -0,0 +1,20 @@
+# 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'
+
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Date < Import::Zendesk::ObjectAttribute
+ def init_callback(_object_attribte)
+ @data_option.merge!(
+ future: true,
+ past: true,
+ diff: 0,
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/decimal.rb b/lib/import/zendesk/object_attribute/decimal.rb
new file mode 100644
index 000000000..6067a7540
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/decimal.rb
@@ -0,0 +1,8 @@
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Decimal < Import::Zendesk::ObjectAttribute::Text
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/dropdown.rb b/lib/import/zendesk/object_attribute/dropdown.rb
new file mode 100644
index 000000000..871e87df1
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/dropdown.rb
@@ -0,0 +1,8 @@
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Dropdown < Import::Zendesk::ObjectAttribute::Select
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/integer.rb b/lib/import/zendesk/object_attribute/integer.rb
new file mode 100644
index 000000000..d16e94233
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/integer.rb
@@ -0,0 +1,25 @@
+# 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'
+
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Integer < Import::Zendesk::ObjectAttribute
+ def init_callback(_object_attribte)
+ @data_option.merge!(
+ min: 0,
+ max: 999_999_999,
+ )
+ end
+
+ private
+
+ def data_type(_attribute)
+ 'integer'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/regexp.rb b/lib/import/zendesk/object_attribute/regexp.rb
new file mode 100644
index 000000000..b557dab66
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/regexp.rb
@@ -0,0 +1,26 @@
+# 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'
+
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Regexp < Import::Zendesk::ObjectAttribute
+ def init_callback(object_attribte)
+ @data_option.merge!(
+ type: 'text',
+ maxlength: 255,
+ regex: object_attribte.regexp_for_validation,
+ )
+ end
+
+ private
+
+ def data_type(_attribute)
+ 'input'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/select.rb b/lib/import/zendesk/object_attribute/select.rb
new file mode 100644
index 000000000..f557aca68
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/select.rb
@@ -0,0 +1,28 @@
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Select < Import::Zendesk::ObjectAttribute
+ def init_callback(object_attribte)
+ @data_option.merge!(
+ default: '',
+ options: options(object_attribte),
+ )
+ end
+
+ private
+
+ def data_type(_attribute)
+ 'select'
+ end
+
+ def options(object_attribte)
+ result = {}
+ object_attribte.custom_field_options.each { |entry|
+ result[ entry['value'] ] = entry['name']
+ }
+ result
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/tagger.rb b/lib/import/zendesk/object_attribute/tagger.rb
new file mode 100644
index 000000000..c8481b586
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/tagger.rb
@@ -0,0 +1,8 @@
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Tagger < Import::Zendesk::ObjectAttribute::Select
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/text.rb b/lib/import/zendesk/object_attribute/text.rb
new file mode 100644
index 000000000..3d68dfcbe
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/text.rb
@@ -0,0 +1,20 @@
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Text < Import::Zendesk::ObjectAttribute
+ def init_callback(_object_attribte)
+ @data_option.merge!(
+ type: 'text',
+ maxlength: 255,
+ )
+ end
+
+ private
+
+ def data_type(_attribute)
+ 'input'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_attribute/textarea.rb b/lib/import/zendesk/object_attribute/textarea.rb
new file mode 100644
index 000000000..f2e6b9013
--- /dev/null
+++ b/lib/import/zendesk/object_attribute/textarea.rb
@@ -0,0 +1,20 @@
+module Import
+ module Zendesk
+ class ObjectAttribute
+ class Textarea < Import::Zendesk::ObjectAttribute
+ def init_callback(_object_attribte)
+ @data_option.merge!(
+ type: 'textarea',
+ maxlength: 255,
+ )
+ end
+
+ private
+
+ def data_type(_attribute)
+ 'input'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/object_field.rb b/lib/import/zendesk/object_field.rb
new file mode 100644
index 000000000..ac0024271
--- /dev/null
+++ b/lib/import/zendesk/object_field.rb
@@ -0,0 +1,39 @@
+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)
+ return @local_name if @local_name
+ @local_name = remote_name(object_field).gsub(/\s/, '_').downcase
+ 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
new file mode 100644
index 000000000..961035c29
--- /dev/null
+++ b/lib/import/zendesk/organization.rb
@@ -0,0 +1,34 @@
+# 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
new file mode 100644
index 000000000..5f1af7584
--- /dev/null
+++ b/lib/import/zendesk/organization_factory.rb
@@ -0,0 +1,10 @@
+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
new file mode 100644
index 000000000..f29fb53ef
--- /dev/null
+++ b/lib/import/zendesk/organization_field.rb
@@ -0,0 +1,6 @@
+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
new file mode 100644
index 000000000..cf235422b
--- /dev/null
+++ b/lib/import/zendesk/organization_field_factory.rb
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 000000000..25b7e9c44
--- /dev/null
+++ b/lib/import/zendesk/priority.rb
@@ -0,0 +1,32 @@
+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
new file mode 100644
index 000000000..7fd433b1d
--- /dev/null
+++ b/lib/import/zendesk/requester.rb
@@ -0,0 +1,39 @@
+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
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/state.rb b/lib/import/zendesk/state.rb
new file mode 100644
index 000000000..25fabd497
--- /dev/null
+++ b/lib/import/zendesk/state.rb
@@ -0,0 +1,29 @@
+module Import
+ module Zendesk
+ class State
+
+ MAPPING = {
+ 'pending' => 'pending reminder',
+ 'solved' => 'closed',
+ }.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
new file mode 100644
index 000000000..bbebfe2e7
--- /dev/null
+++ b/lib/import/zendesk/ticket.rb
@@ -0,0 +1,73 @@
+# 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_attributes(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,
+ 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
new file mode 100644
index 000000000..d6a2f3fff
--- /dev/null
+++ b/lib/import/zendesk/ticket/comment.rb
@@ -0,0 +1,71 @@
+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_attributes(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.empty?
+ 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
new file mode 100644
index 000000000..9c1ec687a
--- /dev/null
+++ b/lib/import/zendesk/ticket/comment/attachment.rb
@@ -0,0 +1,43 @@
+module Import
+ module Zendesk
+ class Ticket
+ class Comment
+ class Attachment
+ extend 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
+ )
+ 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
+ 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
new file mode 100644
index 000000000..474e21501
--- /dev/null
+++ b/lib/import/zendesk/ticket/comment/attachment_factory.rb
@@ -0,0 +1,20 @@
+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
+
+ private
+
+ 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
new file mode 100644
index 000000000..f8d3ab028
--- /dev/null
+++ b/lib/import/zendesk/ticket/comment/sender.rb
@@ -0,0 +1,49 @@
+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
new file mode 100644
index 000000000..8dad31905
--- /dev/null
+++ b/lib/import/zendesk/ticket/comment/type.rb
@@ -0,0 +1,60 @@
+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
new file mode 100644
index 000000000..99f96429d
--- /dev/null
+++ b/lib/import/zendesk/ticket/comment_factory.rb
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 000000000..aa67e0861
--- /dev/null
+++ b/lib/import/zendesk/ticket/sub_object_factory.rb
@@ -0,0 +1,21 @@
+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
new file mode 100644
index 000000000..f4a07a079
--- /dev/null
+++ b/lib/import/zendesk/ticket/tag.rb
@@ -0,0 +1,16 @@
+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
new file mode 100644
index 000000000..7fed1c5d8
--- /dev/null
+++ b/lib/import/zendesk/ticket/tag_factory.rb
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 000000000..0362f1f41
--- /dev/null
+++ b/lib/import/zendesk/ticket_factory.rb
@@ -0,0 +1,7 @@
+module Import
+ module Zendesk
+ module TicketFactory
+ extend Import::Zendesk::BaseFactory
+ end
+ end
+end
diff --git a/lib/import/zendesk/ticket_field.rb b/lib/import/zendesk/ticket_field.rb
new file mode 100644
index 000000000..1a84bd374
--- /dev/null
+++ b/lib/import/zendesk/ticket_field.rb
@@ -0,0 +1,12 @@
+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
new file mode 100644
index 000000000..b85dd8eb5
--- /dev/null
+++ b/lib/import/zendesk/ticket_field_factory.rb
@@ -0,0 +1,33 @@
+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',
+ 'group' => 'group_id',
+ 'assignee' => 'owner_id',
+ }.freeze
+
+ # rubocop:disable Style/ModuleFunction
+ extend self
+
+ def skip?(field)
+ # 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
new file mode 100644
index 000000000..bda0b4d8c
--- /dev/null
+++ b/lib/import/zendesk/user.rb
@@ -0,0 +1,75 @@
+# 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
new file mode 100644
index 000000000..0fe82e247
--- /dev/null
+++ b/lib/import/zendesk/user/group.rb
@@ -0,0 +1,51 @@
+# 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].empty?
+
+ mapping[user.id].each { |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)
+ }
+ groups
+ end
+
+ private
+
+ def mapping
+
+ return @mapping if !@mapping.nil?
+
+ @mapping = {}
+
+ Import::Zendesk::Requester.client.group_memberships.all! { |group_membership|
+
+ @mapping[ group_membership.user_id ] ||= []
+ @mapping[ group_membership.user_id ].push( group_membership.group_id )
+ }
+
+ @mapping
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/user/role.rb b/lib/import/zendesk/user/role.rb
new file mode 100644
index 000000000..e5b389b47
--- /dev/null
+++ b/lib/import/zendesk/user/role.rb
@@ -0,0 +1,62 @@
+# 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
+ return @role_admin if @role_admin
+ @role_admin = ::Role.lookup(name: 'Admin')
+ end
+
+ def role_agent
+ return @role_agent if @role_agent
+ @role_agent = ::Role.lookup(name: 'Agent')
+ end
+
+ def role_customer
+ return @role_customer if @role_customer
+ @role_customer = ::Role.lookup(name: 'Customer')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/import/zendesk/user_factory.rb b/lib/import/zendesk/user_factory.rb
new file mode 100644
index 000000000..ec3fafe5b
--- /dev/null
+++ b/lib/import/zendesk/user_factory.rb
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 000000000..5b8f615f1
--- /dev/null
+++ b/lib/import/zendesk/user_field.rb
@@ -0,0 +1,6 @@
+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
new file mode 100644
index 000000000..101d50178
--- /dev/null
+++ b/lib/import/zendesk/user_field_factory.rb
@@ -0,0 +1,8 @@
+module Import
+ module Zendesk
+ module UserFieldFactory
+ extend Import::Zendesk::BaseFactory
+ extend Import::Zendesk::LocalIDMapperHook
+ end
+ end
+end
diff --git a/spec/import/otrs/async_examples.rb b/spec/import/async_examples.rb
similarity index 80%
rename from spec/import/otrs/async_examples.rb
rename to spec/import/async_examples.rb
index 4ae347102..974d8b63f 100644
--- a/spec/import/otrs/async_examples.rb
+++ b/spec/import/async_examples.rb
@@ -1,4 +1,4 @@
-RSpec.shared_examples 'Import::OTRS::Async' do
+RSpec.shared_examples 'Import::Async' do
it 'responds to start_bg' do
expect(described_class).to respond_to('start_bg')
end
diff --git a/spec/import/base_factory_examples.rb b/spec/import/base_factory_examples.rb
index ca15fafcd..173644dcc 100644
--- a/spec/import/base_factory_examples.rb
+++ b/spec/import/base_factory_examples.rb
@@ -6,6 +6,9 @@ RSpec.shared_examples 'Import::BaseFactory' do
it 'responds to pre_import_hook' do
expect(described_class).to respond_to('pre_import_hook')
end
+ it 'responds to post_import_hook' do
+ expect(described_class).to respond_to('post_import_hook')
+ end
it 'responds to backend_class' do
expect(described_class).to respond_to('backend_class')
end
@@ -18,7 +21,7 @@ RSpec.shared_examples 'Import::BaseFactory extender' do
it 'calls new on determined backend object' do
record = double()
expect(described_class).to receive(:backend_class).and_return(Class)
- expect(Class).to receive(:new).with(record)
+ expect(Class).to receive(:new).with(record, any_args)
described_class.import([record])
end
end
diff --git a/spec/import/otrs/import_stats_examples.rb b/spec/import/import_stats_examples.rb
similarity index 78%
rename from spec/import/otrs/import_stats_examples.rb
rename to spec/import/import_stats_examples.rb
index 7cbb7ea59..dc08268ca 100644
--- a/spec/import/otrs/import_stats_examples.rb
+++ b/spec/import/import_stats_examples.rb
@@ -1,4 +1,4 @@
-RSpec.shared_examples 'Import::OTRS::ImportStats' do
+RSpec.shared_examples 'Import::ImportStats' do
it 'responds to current_state' do
expect(described_class).to respond_to('current_state')
end
diff --git a/spec/import/otrs/priority_factory_spec.rb b/spec/import/otrs/priority_factory_spec.rb
index 34033008c..0f3d82288 100644
--- a/spec/import/otrs/priority_factory_spec.rb
+++ b/spec/import/otrs/priority_factory_spec.rb
@@ -3,4 +3,13 @@ require 'import/factory_examples'
RSpec.describe Import::OTRS::PriorityFactory do
it_behaves_like 'Import::Factory'
+
+ it 'imports records' do
+
+ import_data = {
+ name: 'test',
+ }
+ expect(::Import::OTRS::Priority).to receive(:new).with(import_data)
+ described_class.import([import_data])
+ end
end
diff --git a/spec/import/otrs_spec.rb b/spec/import/otrs_spec.rb
index 95bb5a10f..efe89745e 100644
--- a/spec/import/otrs_spec.rb
+++ b/spec/import/otrs_spec.rb
@@ -1,14 +1,14 @@
require 'rails_helper'
require 'import/helper_examples'
require 'import/importer_examples'
-require 'import/otrs/async_examples'
require 'import/otrs/diff_examples'
-require 'import/otrs/import_stats_examples'
+require 'import/async_examples'
+require 'import/import_stats_examples'
RSpec.describe Import::OTRS do
it_behaves_like 'Import backend'
+ it_behaves_like 'Import::Async'
it_behaves_like 'Import::Helper'
- it_behaves_like 'Import::OTRS::Async'
+ it_behaves_like 'Import::ImportStats'
it_behaves_like 'Import::OTRS::Diff'
- it_behaves_like 'Import::OTRS::ImportStats'
end
diff --git a/spec/import/zendesk/base_factory_examples.rb b/spec/import/zendesk/base_factory_examples.rb
new file mode 100644
index 000000000..adae42f6d
--- /dev/null
+++ b/spec/import/zendesk/base_factory_examples.rb
@@ -0,0 +1,23 @@
+require '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/import/zendesk/base_factory_spec.rb b/spec/import/zendesk/base_factory_spec.rb
new file mode 100644
index 000000000..18a6cc412
--- /dev/null
+++ b/spec/import/zendesk/base_factory_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require 'import/zendesk/base_factory_examples'
+
+RSpec.describe Import::Zendesk::BaseFactory do
+ it_behaves_like 'Import::Zendesk::BaseFactory'
+end
diff --git a/spec/import/zendesk/group_factory_spec.rb b/spec/import/zendesk/group_factory_spec.rb
new file mode 100644
index 000000000..3ab082675
--- /dev/null
+++ b/spec/import/zendesk/group_factory_spec.rb
@@ -0,0 +1,8 @@
+require 'rails_helper'
+require 'import/zendesk/base_factory_examples'
+require '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/import/zendesk/group_spec.rb b/spec/import/zendesk/group_spec.rb
new file mode 100644
index 000000000..014f0e405
--- /dev/null
+++ b/spec/import/zendesk/group_spec.rb
@@ -0,0 +1,25 @@
+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/import/zendesk/local_id_mapper_hook_examples.rb b/spec/import/zendesk/local_id_mapper_hook_examples.rb
new file mode 100644
index 000000000..27dfddfe8
--- /dev/null
+++ b/spec/import/zendesk/local_id_mapper_hook_examples.rb
@@ -0,0 +1,20 @@
+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/import/zendesk/local_id_mapper_hook_spec.rb b/spec/import/zendesk/local_id_mapper_hook_spec.rb
new file mode 100644
index 000000000..8d527a67c
--- /dev/null
+++ b/spec/import/zendesk/local_id_mapper_hook_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require 'import/zendesk/local_id_mapper_hook_examples'
+
+RSpec.describe Import::Zendesk::LocalIDMapperHook do
+ it_behaves_like 'Import::Zendesk::LocalIDMapperHook'
+end
diff --git a/spec/import/zendesk/lookup_backend_examples.rb b/spec/import/zendesk/lookup_backend_examples.rb
new file mode 100644
index 000000000..f1e8220ff
--- /dev/null
+++ b/spec/import/zendesk/lookup_backend_examples.rb
@@ -0,0 +1,5 @@
+RSpec.shared_examples 'Lookup backend' do
+ it 'responds to lookup' do
+ expect(described_class).to respond_to('lookup')
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/checkbox_spec.rb b/spec/import/zendesk/object_attribute/checkbox_spec.rb
new file mode 100644
index 000000000..be31043b0
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/checkbox_spec.rb
@@ -0,0 +1,58 @@
+require 'rails_helper'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Checkbox do
+
+ it 'imports boolean object attribute from checkbox object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'checkbox',
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'boolean',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ default: false,
+ options: {
+ true => 'yes',
+ false => 'no'
+ }
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/date_spec.rb b/spec/import/zendesk/object_attribute/date_spec.rb
new file mode 100644
index 000000000..fe19ba525
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/date_spec.rb
@@ -0,0 +1,59 @@
+require 'rails_helper'
+
+# required due to some of rails autoloading issues
+require 'import/zendesk/object_attribute/date'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Date do
+
+ it 'imports date object attribute from date object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'date',
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'date',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ future: true,
+ past: true,
+ diff: 0,
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/decimal_spec.rb b/spec/import/zendesk/object_attribute/decimal_spec.rb
new file mode 100644
index 000000000..37014e780
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/decimal_spec.rb
@@ -0,0 +1,55 @@
+require 'rails_helper'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Decimal do
+
+ it 'imports input object attribute from decimal object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'decimal',
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'input',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ type: 'text',
+ maxlength: 255,
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/dropdown_spec.rb b/spec/import/zendesk/object_attribute/dropdown_spec.rb
new file mode 100644
index 000000000..6bc10fba9
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/dropdown_spec.rb
@@ -0,0 +1,70 @@
+require 'rails_helper'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Dropdown do
+
+ it 'imports select object attribute from dropdown object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'dropdown',
+ custom_field_options: [
+ {
+ 'id' => 1,
+ 'value' => 'Key 1',
+ 'name' => 'Value 1'
+ },
+ {
+ 'id' => 2,
+ 'value' => 'Key 2',
+ 'name' => 'Value 2'
+ },
+ ]
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'select',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ default: '',
+ options: {
+ 'Key 1' => 'Value 1',
+ 'Key 2' => 'Value 2'
+ },
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ created_instance = described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/integer_spec.rb b/spec/import/zendesk/object_attribute/integer_spec.rb
new file mode 100644
index 000000000..964d1e7a6
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/integer_spec.rb
@@ -0,0 +1,58 @@
+require 'rails_helper'
+
+# required due to some of rails autoloading issues
+require 'import/zendesk/object_attribute/integer'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Integer do
+
+ it 'imports integer object attribute from integer object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'integer',
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'integer',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ min: 0,
+ max: 999_999_999,
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/regexp_spec.rb b/spec/import/zendesk/object_attribute/regexp_spec.rb
new file mode 100644
index 000000000..85b30908c
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/regexp_spec.rb
@@ -0,0 +1,61 @@
+require 'rails_helper'
+
+# required due to some of rails autoloading issues
+require 'import/zendesk/object_attribute/regexp'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Regexp do
+
+ it 'imports input object attribute from regexp object field' do
+
+ regex = '.+?'
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'regexp',
+ regexp_for_validation: regex
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'input',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ type: 'text',
+ maxlength: 255,
+ regex: regex,
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/tagger_spec.rb b/spec/import/zendesk/object_attribute/tagger_spec.rb
new file mode 100644
index 000000000..f5952f47b
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/tagger_spec.rb
@@ -0,0 +1,70 @@
+require 'rails_helper'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Tagger do
+
+ it 'imports select object attribute from tagger object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'tagger',
+ custom_field_options: [
+ {
+ 'id' => 1,
+ 'value' => 'Key 1',
+ 'name' => 'Value 1'
+ },
+ {
+ 'id' => 2,
+ 'value' => 'Key 2',
+ 'name' => 'Value 2'
+ },
+ ]
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'select',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ default: '',
+ options: {
+ 'Key 1' => 'Value 1',
+ 'Key 2' => 'Value 2'
+ },
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ created_instance = described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/text_spec.rb b/spec/import/zendesk/object_attribute/text_spec.rb
new file mode 100644
index 000000000..5648a65a0
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/text_spec.rb
@@ -0,0 +1,55 @@
+require 'rails_helper'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Text do
+
+ it 'imports input object attribute from text object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'text',
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'input',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ type: 'text',
+ maxlength: 255,
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute/textarea_spec.rb b/spec/import/zendesk/object_attribute/textarea_spec.rb
new file mode 100644
index 000000000..f66209599
--- /dev/null
+++ b/spec/import/zendesk/object_attribute/textarea_spec.rb
@@ -0,0 +1,55 @@
+require 'rails_helper'
+
+RSpec.describe Import::Zendesk::ObjectAttribute::Textarea do
+
+ it 'imports input object attribute from textarea object field' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'textarea',
+ )
+
+ expected_structure = {
+ object: 'Ticket',
+ name: 'example_field',
+ display: 'Example attribute',
+ data_type: 'input',
+ data_option: {
+ null: false,
+ note: 'Example attribute description',
+ type: 'textarea',
+ maxlength: 255,
+ },
+ editable: true,
+ active: true,
+ screens: {
+ edit: {
+ Customer: {
+ shown: true,
+ null: false
+ },
+ view: {
+ '-all-' => {
+ shown: true
+ }
+ }
+ }
+ },
+ position: 12,
+ created_by_id: 1,
+ updated_by_id: 1
+ }
+
+ expect(ObjectManager::Attribute).to receive(:add).with(expected_structure)
+ expect(ObjectManager::Attribute).to receive(:migration_execute)
+
+ described_class.new('Ticket', 'example_field', attribute)
+ end
+end
diff --git a/spec/import/zendesk/object_attribute_spec.rb b/spec/import/zendesk/object_attribute_spec.rb
new file mode 100644
index 000000000..4fb38161d
--- /dev/null
+++ b/spec/import/zendesk/object_attribute_spec.rb
@@ -0,0 +1,21 @@
+require 'rails_helper'
+
+RSpec.describe Import::Zendesk::ObjectAttribute do
+
+ it 'throws an exception if no init_callback is implemented' do
+
+ attribute = double(
+ title: 'Example attribute',
+ description: 'Example attribute description',
+ removable: false,
+ active: true,
+ position: 12,
+ visible_in_portal: true,
+ required_in_portal: true,
+ required: true,
+ type: 'input',
+ )
+
+ expect { described_class.new('Ticket', 'example_field', attribute) }.to raise_error(RuntimeError)
+ end
+end
diff --git a/spec/import/zendesk/object_field_examples.rb b/spec/import/zendesk/object_field_examples.rb
new file mode 100644
index 000000000..6ad9993d6
--- /dev/null
+++ b/spec/import/zendesk/object_field_examples.rb
@@ -0,0 +1,22 @@
+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/import/zendesk/object_field_spec.rb b/spec/import/zendesk/object_field_spec.rb
new file mode 100644
index 000000000..6cafe2c96
--- /dev/null
+++ b/spec/import/zendesk/object_field_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require 'import/zendesk/object_field_examples'
+
+RSpec.describe Import::Zendesk::ObjectField do
+ it_behaves_like 'Import::Zendesk::ObjectField'
+end
diff --git a/spec/import/zendesk/organization_factory_spec.rb b/spec/import/zendesk/organization_factory_spec.rb
new file mode 100644
index 000000000..a3264ee71
--- /dev/null
+++ b/spec/import/zendesk/organization_factory_spec.rb
@@ -0,0 +1,8 @@
+require 'rails_helper'
+require 'import/factory_examples'
+require '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/import/zendesk/organization_field_factory_spec.rb b/spec/import/zendesk/organization_field_factory_spec.rb
new file mode 100644
index 000000000..78d3eeee3
--- /dev/null
+++ b/spec/import/zendesk/organization_field_factory_spec.rb
@@ -0,0 +1,8 @@
+require 'rails_helper'
+require 'import/zendesk/base_factory_examples'
+require '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/import/zendesk/organization_field_spec.rb b/spec/import/zendesk/organization_field_spec.rb
new file mode 100644
index 000000000..df323d8c4
--- /dev/null
+++ b/spec/import/zendesk/organization_field_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require 'import/zendesk/object_field_examples'
+
+RSpec.describe Import::Zendesk::OrganizationField do
+ it_behaves_like 'Import::Zendesk::ObjectField'
+end
diff --git a/spec/import/zendesk/organization_spec.rb b/spec/import/zendesk/organization_spec.rb
new file mode 100644
index 000000000..24ba4f850
--- /dev/null
+++ b/spec/import/zendesk/organization_spec.rb
@@ -0,0 +1,27 @@
+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/import/zendesk/priority_spec.rb b/spec/import/zendesk/priority_spec.rb
new file mode 100644
index 000000000..5ab940aff
--- /dev/null
+++ b/spec/import/zendesk/priority_spec.rb
@@ -0,0 +1,14 @@
+require 'rails_helper'
+require '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/import/zendesk/state_spec.rb b/spec/import/zendesk/state_spec.rb
new file mode 100644
index 000000000..075cb724d
--- /dev/null
+++ b/spec/import/zendesk/state_spec.rb
@@ -0,0 +1,14 @@
+require 'rails_helper'
+require '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/import/zendesk/ticket/comment/attachment_factory_spec.rb b/spec/import/zendesk/ticket/comment/attachment_factory_spec.rb
new file mode 100644
index 000000000..6b5daef23
--- /dev/null
+++ b/spec/import/zendesk/ticket/comment/attachment_factory_spec.rb
@@ -0,0 +1,19 @@
+require 'rails_helper'
+require '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()
+ expect(Class).to receive(:new).with(record, local_article)
+ parameter = double()
+ expect(parameter).to receive(:each).and_yield(record)
+ described_class.import(parameter, local_article)
+ end
+end
diff --git a/spec/import/zendesk/ticket/comment/attachment_spec.rb b/spec/import/zendesk/ticket/comment/attachment_spec.rb
new file mode 100644
index 000000000..5376e8059
--- /dev/null
+++ b/spec/import/zendesk/ticket/comment/attachment_spec.rb
@@ -0,0 +1,37 @@
+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/import/zendesk/ticket/comment/local_id_lookup_backend_examples.rb b/spec/import/zendesk/ticket/comment/local_id_lookup_backend_examples.rb
new file mode 100644
index 000000000..254eaaa9b
--- /dev/null
+++ b/spec/import/zendesk/ticket/comment/local_id_lookup_backend_examples.rb
@@ -0,0 +1,7 @@
+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/import/zendesk/ticket/comment/sender_spec.rb b/spec/import/zendesk/ticket/comment/sender_spec.rb
new file mode 100644
index 000000000..283359857
--- /dev/null
+++ b/spec/import/zendesk/ticket/comment/sender_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require '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/import/zendesk/ticket/comment/type_spec.rb b/spec/import/zendesk/ticket/comment/type_spec.rb
new file mode 100644
index 000000000..2af45a32e
--- /dev/null
+++ b/spec/import/zendesk/ticket/comment/type_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require '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/import/zendesk/ticket/comment_factory_spec.rb b/spec/import/zendesk/ticket/comment_factory_spec.rb
new file mode 100644
index 000000000..3148948be
--- /dev/null
+++ b/spec/import/zendesk/ticket/comment_factory_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require '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/import/zendesk/ticket/comment_spec.rb b/spec/import/zendesk/ticket/comment_spec.rb
new file mode 100644
index 000000000..8b49efe8a
--- /dev/null
+++ b/spec/import/zendesk/ticket/comment_spec.rb
@@ -0,0 +1,637 @@
+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_attributes).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_attributes).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_attributes).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_attributes).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_attributes).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_attributes).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_attributes).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/import/zendesk/ticket/sub_object_factory_examples.rb b/spec/import/zendesk/ticket/sub_object_factory_examples.rb
new file mode 100644
index 000000000..afd8502ac
--- /dev/null
+++ b/spec/import/zendesk/ticket/sub_object_factory_examples.rb
@@ -0,0 +1,21 @@
+require 'rails_helper'
+require '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/import/zendesk/ticket/tag_factory_spec.rb b/spec/import/zendesk/ticket/tag_factory_spec.rb
new file mode 100644
index 000000000..02011c259
--- /dev/null
+++ b/spec/import/zendesk/ticket/tag_factory_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require '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/import/zendesk/ticket/tag_spec.rb b/spec/import/zendesk/ticket/tag_spec.rb
new file mode 100644
index 000000000..62bad3e2c
--- /dev/null
+++ b/spec/import/zendesk/ticket/tag_spec.rb
@@ -0,0 +1,21 @@
+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/import/zendesk/ticket_factory_spec.rb b/spec/import/zendesk/ticket_factory_spec.rb
new file mode 100644
index 000000000..d92f06c4a
--- /dev/null
+++ b/spec/import/zendesk/ticket_factory_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require 'import/zendesk/base_factory_examples'
+
+RSpec.describe Import::Zendesk::TicketFactory do
+ it_behaves_like 'Import::Zendesk::BaseFactory'
+end
diff --git a/spec/import/zendesk/ticket_field_factory_spec.rb b/spec/import/zendesk/ticket_field_factory_spec.rb
new file mode 100644
index 000000000..63d26ccf1
--- /dev/null
+++ b/spec/import/zendesk/ticket_field_factory_spec.rb
@@ -0,0 +1,8 @@
+require 'rails_helper'
+require 'import/zendesk/base_factory_examples'
+require '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/import/zendesk/ticket_field_spec.rb b/spec/import/zendesk/ticket_field_spec.rb
new file mode 100644
index 000000000..508791f71
--- /dev/null
+++ b/spec/import/zendesk/ticket_field_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require 'import/zendesk/object_field_examples'
+
+RSpec.describe Import::Zendesk::TicketField do
+ it_behaves_like 'Import::Zendesk::ObjectField'
+end
diff --git a/spec/import/zendesk/ticket_spec.rb b/spec/import/zendesk/ticket_spec.rb
new file mode 100644
index 000000000..94604708d
--- /dev/null
+++ b/spec/import/zendesk/ticket_spec.rb
@@ -0,0 +1,123 @@
+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',
+ 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,
+ 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::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',
+ 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,
+ 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::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_attributes).with(expected_structure)
+
+ created_instance = described_class.new(ticket)
+ end
+end
diff --git a/spec/import/zendesk/user/group_spec.rb b/spec/import/zendesk/user/group_spec.rb
new file mode 100644
index 000000000..d2100040f
--- /dev/null
+++ b/spec/import/zendesk/user/group_spec.rb
@@ -0,0 +1,9 @@
+require 'rails_helper'
+require '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/import/zendesk/user/lookup_backend_examples.rb b/spec/import/zendesk/user/lookup_backend_examples.rb
new file mode 100644
index 000000000..47ef2f94c
--- /dev/null
+++ b/spec/import/zendesk/user/lookup_backend_examples.rb
@@ -0,0 +1,7 @@
+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/import/zendesk/user/role_spec.rb b/spec/import/zendesk/user/role_spec.rb
new file mode 100644
index 000000000..5bf383d4f
--- /dev/null
+++ b/spec/import/zendesk/user/role_spec.rb
@@ -0,0 +1,13 @@
+require 'rails_helper'
+require '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/import/zendesk/user_factory_spec.rb b/spec/import/zendesk/user_factory_spec.rb
new file mode 100644
index 000000000..e77608c89
--- /dev/null
+++ b/spec/import/zendesk/user_factory_spec.rb
@@ -0,0 +1,8 @@
+require 'rails_helper'
+require 'import/zendesk/base_factory_examples'
+require '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/import/zendesk/user_field_factory_spec.rb b/spec/import/zendesk/user_field_factory_spec.rb
new file mode 100644
index 000000000..03dc38cec
--- /dev/null
+++ b/spec/import/zendesk/user_field_factory_spec.rb
@@ -0,0 +1,8 @@
+require 'rails_helper'
+require 'import/zendesk/base_factory_examples'
+require '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/import/zendesk/user_field_spec.rb b/spec/import/zendesk/user_field_spec.rb
new file mode 100644
index 000000000..491c3af88
--- /dev/null
+++ b/spec/import/zendesk/user_field_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+require 'import/zendesk/object_field_examples'
+
+RSpec.describe Import::Zendesk::UserField do
+ it_behaves_like 'Import::Zendesk::ObjectField'
+end
diff --git a/spec/import/zendesk/user_spec.rb b/spec/import/zendesk/user_spec.rb
new file mode 100644
index 000000000..3c3d43eaa
--- /dev/null
+++ b/spec/import/zendesk/user_spec.rb
@@ -0,0 +1,165 @@
+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/import/zendesk_spec.rb b/spec/import/zendesk_spec.rb
index 7404bbe04..35ef0ea0f 100644
--- a/spec/import/zendesk_spec.rb
+++ b/spec/import/zendesk_spec.rb
@@ -1,6 +1,12 @@
require 'rails_helper'
+require 'import/helper_examples'
require 'import/importer_examples'
+require 'import/async_examples'
+require '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/test/integration/zendesk_import_browser_test.rb b/test/integration/zendesk_import_browser_test.rb
index d2765a07b..156e8f362 100644
--- a/test/integration/zendesk_import_browser_test.rb
+++ b/test/integration/zendesk_import_browser_test.rb
@@ -99,7 +99,7 @@ class ZendeskImportBrowserTest < TestCase
watch_for(
css: '.js-ticket .js-done',
value: '143',
- timeout: 300,
+ timeout: 600,
)
watch_for(
diff --git a/test/integration/zendesk_import_test.rb b/test/integration/zendesk_import_test.rb
index 8be3b7525..13be8dd44 100644
--- a/test/integration/zendesk_import_test.rb
+++ b/test/integration/zendesk_import_test.rb
@@ -72,7 +72,7 @@ class ZendeskImportTest < ActiveSupport::TestCase
data: {
firstname: 'Bob',
lastname: 'Smith',
- login: '1150734731',
+ login: 'bob.smith@znuny.com',
email: 'bob.smith@znuny.com',
active: true,
phone: '00114124',
@@ -86,7 +86,7 @@ class ZendeskImportTest < ActiveSupport::TestCase
data: {
firstname: 'Hansimerkur',
lastname: '',
- login: '1202726471',
+ login: 'hansimerkur@znuny.com',
email: 'hansimerkur@znuny.com',
active: true,
lieblingstier: nil,
@@ -99,7 +99,7 @@ class ZendeskImportTest < ActiveSupport::TestCase
data: {
firstname: 'Bernd',
lastname: 'Hofbecker',
- login: '1202726611',
+ login: 'bernd.hofbecker@znuny.com',
email: 'bernd.hofbecker@znuny.com',
active: true,
},
@@ -111,7 +111,7 @@ class ZendeskImportTest < ActiveSupport::TestCase
data: {
firstname: 'Zendesk',
lastname: '',
- login: '1202737821',
+ login: 'noreply@zendesk.com',
email: 'noreply@zendesk.com',
active: true,
},
@@ -123,7 +123,7 @@ class ZendeskImportTest < ActiveSupport::TestCase
data: {
firstname: 'Hans',
lastname: 'Peter Wurst',
- login: '1205512622',
+ login: 'hansimerkur+zd-c1@znuny.com',
email: 'hansimerkur+zd-c1@znuny.com',
active: true,
},