Fixes #4052 - Freshdesk migration which doesn't support time entries runs in unwanted behaviour.
This commit is contained in:
parent
52834f0d85
commit
3cada0f15a
18 changed files with 242 additions and 45 deletions
|
@ -11,6 +11,7 @@ class Sequencer
|
|||
'Import::Common::ImportMode::Check',
|
||||
'Import::Common::SystemInitDone::Check',
|
||||
'Import::Common::ImportJob::DryRun',
|
||||
'Import::Freshdesk::TimeEntry::Available',
|
||||
'Import::Freshdesk::IdMap',
|
||||
'Import::Freshdesk::Groups',
|
||||
'Import::Freshdesk::FieldMap',
|
||||
|
|
|
@ -8,6 +8,7 @@ class Sequencer
|
|||
|
||||
def self.sequence
|
||||
[
|
||||
'Import::Freshdesk::TimeEntry::Skip',
|
||||
'Import::Freshdesk::Request',
|
||||
'Import::Freshdesk::Resources',
|
||||
'Import::Freshdesk::ModelClass',
|
||||
|
|
|
@ -5,6 +5,9 @@ class Sequencer
|
|||
module Import
|
||||
module Freshdesk
|
||||
class ModelClass < Sequencer::Unit::Common::Provider::Named
|
||||
prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action
|
||||
|
||||
skip_action :skipped, :failed
|
||||
|
||||
uses :object
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ class Sequencer
|
|||
|
||||
skip_action :skipped, :failed
|
||||
|
||||
uses :resources, :object, :import_job, :dry_run, :field_map, :id_map
|
||||
uses :resources, :object, :import_job, :dry_run, :field_map, :id_map, :time_entry_available
|
||||
|
||||
def process
|
||||
resources.each do |resource|
|
||||
|
@ -20,6 +20,7 @@ class Sequencer
|
|||
resource: resource,
|
||||
field_map: field_map,
|
||||
id_map: id_map,
|
||||
time_entry_available: time_entry_available,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,9 @@ class Sequencer
|
|||
module Freshdesk
|
||||
class Request < Sequencer::Unit::Common::Provider::Attribute
|
||||
extend ::Sequencer::Unit::Import::Freshdesk::Requester
|
||||
prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action
|
||||
|
||||
skip_action :skipped, :failed
|
||||
|
||||
uses :object, :request_params
|
||||
provides :response
|
||||
|
@ -13,7 +16,6 @@ class Sequencer
|
|||
private
|
||||
|
||||
def response
|
||||
|
||||
builder = backend.new(
|
||||
object: object,
|
||||
request_params: request_params
|
||||
|
|
|
@ -31,12 +31,14 @@ class Sequencer
|
|||
else
|
||||
logger.info "Unknown response: #{response.inspect}. Sleeping 10 seconds and retry (##{iteration + 1}/10)."
|
||||
end
|
||||
|
||||
sleep sleep_for
|
||||
end
|
||||
|
||||
def handle_exception(e, iteration)
|
||||
logger.error e
|
||||
logger.info "Sleeping 10 seconds after #{e.class.name} and retry (##{iteration + 1}/10)."
|
||||
|
||||
sleep 10
|
||||
end
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ class Sequencer
|
|||
module Freshdesk
|
||||
class Resources < Sequencer::Unit::Common::Provider::Named
|
||||
include ::Sequencer::Unit::Import::Common::Model::Mixin::HandleFailure
|
||||
prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action
|
||||
|
||||
skip_action :skipped, :failed
|
||||
|
||||
uses :response, :skipped_resource_id
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class Sequencer
|
|||
module SubSequence
|
||||
class Generic < Sequencer::Unit::Base
|
||||
|
||||
uses :dry_run, :import_job, :field_map, :id_map
|
||||
uses :dry_run, :import_job, :field_map, :id_map, :time_entry_available
|
||||
|
||||
attr_accessor :iteration, :result
|
||||
|
||||
|
@ -25,6 +25,7 @@ class Sequencer
|
|||
field_map: field_map,
|
||||
id_map: id_map,
|
||||
skipped_resource_id: skipped_resource_id,
|
||||
time_entry_available: time_entry_available,
|
||||
},
|
||||
expecting: self.class.const_get(:EXPECTING))
|
||||
break if iteration_should_stop?
|
||||
|
@ -56,7 +57,7 @@ class Sequencer
|
|||
end
|
||||
|
||||
def iteration_should_stop?
|
||||
return true if result[:action] == :failed
|
||||
return true if result[:action] == :failed || result[:action] == :skipped
|
||||
return true if result[:response].header['link'].blank?
|
||||
|
||||
false
|
||||
|
|
|
@ -8,8 +8,6 @@ class Sequencer
|
|||
class TimeEntries < Sequencer::Unit::Import::Freshdesk::SubSequence::Generic
|
||||
prepend ::Sequencer::Unit::Import::Common::Model::Mixin::Skip::Action
|
||||
|
||||
optional :action
|
||||
|
||||
skip_action :skipped, :failed
|
||||
|
||||
uses :resource
|
||||
|
|
34
lib/sequencer/unit/import/freshdesk/time_entry/available.rb
Normal file
34
lib/sequencer/unit/import/freshdesk/time_entry/available.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
class Sequencer
|
||||
class Unit
|
||||
module Import
|
||||
module Freshdesk
|
||||
module TimeEntry
|
||||
class Available < Sequencer::Unit::Common::Provider::Attribute
|
||||
extend ::Sequencer::Unit::Import::Freshdesk::Requester
|
||||
|
||||
provides :time_entry_available
|
||||
|
||||
def process
|
||||
state.provide(:time_entry_available, time_entry_available)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def time_entry_available
|
||||
response = self.class.perform_request(
|
||||
api_path: 'time_entries',
|
||||
)
|
||||
|
||||
response.is_a?(Net::HTTPOK)
|
||||
rescue => e
|
||||
logger.info e
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,6 @@ class Sequencer
|
|||
include ::Sequencer::Unit::Import::Common::Mapping::Mixin::ProvideMapped
|
||||
|
||||
uses :resource, :id_map
|
||||
provides :action
|
||||
|
||||
def process
|
||||
provide_mapped do
|
||||
|
@ -21,14 +20,6 @@ class Sequencer
|
|||
updated_at: resource['updated_at'],
|
||||
}
|
||||
end
|
||||
rescue TypeError => e
|
||||
# TimeTracking is not available in the plans: Sprout, Blossom
|
||||
# In this case `resource`s value is `["code", "require_feature"]`
|
||||
# See:
|
||||
# - Ticket# 1077135
|
||||
# - https://support.freshdesk.com/support/solutions/articles/37583-keeping-track-of-time-spent
|
||||
logger.debug { e }
|
||||
state.provide(:action, :skipped)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
22
lib/sequencer/unit/import/freshdesk/time_entry/skip.rb
Normal file
22
lib/sequencer/unit/import/freshdesk/time_entry/skip.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
class Sequencer
|
||||
class Unit
|
||||
module Import
|
||||
module Freshdesk
|
||||
module TimeEntry
|
||||
class Skip < Sequencer::Unit::Base
|
||||
uses :time_entry_available
|
||||
provides :action
|
||||
|
||||
def process
|
||||
return if time_entry_available
|
||||
|
||||
state.provide(:action, :skipped)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -40,6 +40,7 @@ RSpec.describe ::Sequencer::Sequence::Import::Freshdesk::GenericObject, sequence
|
|||
field_map: {},
|
||||
id_map: {},
|
||||
skipped_resource_id: nil,
|
||||
time_entry_available: true,
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ RSpec.describe ::Sequencer::Sequence::Import::Freshdesk::Ticket, sequencer: :seq
|
|||
resource: resource,
|
||||
field_map: field_map,
|
||||
id_map: id_map,
|
||||
time_entry_available: true,
|
||||
}
|
||||
end
|
||||
let(:owner) { create :agent, group_ids: [group.id] }
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ::Sequencer::Sequence::Import::Freshdesk::TimeEntries, sequencer: :sequence, db_strategy: 'reset' do
|
||||
let(:time_entry_available) { true }
|
||||
let(:ticket) { create :ticket }
|
||||
|
||||
let(:process_payload) do
|
||||
{
|
||||
import_job: build_stubbed(:import_job, name: 'Import::Freshdesk', payload: {}),
|
||||
dry_run: false,
|
||||
object: 'TimeEntry',
|
||||
request_params: {
|
||||
ticket: {
|
||||
'id' => 1001,
|
||||
},
|
||||
},
|
||||
field_map: {},
|
||||
id_map: {
|
||||
'Ticket' => {
|
||||
1001 => ticket.id,
|
||||
},
|
||||
'User' => {
|
||||
80_014_400_475 => 1,
|
||||
}
|
||||
},
|
||||
skipped_resource_id: nil,
|
||||
time_entry_available: time_entry_available,
|
||||
}
|
||||
end
|
||||
|
||||
context 'when time entry feature is available' do
|
||||
let(:resources_payloud) do
|
||||
[
|
||||
{
|
||||
'id' => 80_027_218_656,
|
||||
'billable' => true,
|
||||
'note' => 'Example Preparation',
|
||||
'timer_running' => false,
|
||||
'agent_id' => 80_014_400_475,
|
||||
'ticket_id' => 1001,
|
||||
'time_spent' => '01:20',
|
||||
'created_at' => '2021-05-14T12:29:27Z',
|
||||
'updated_at' => '2021-05-14T12:29:27Z',
|
||||
'start_time' => '2021-05-14T12:29:27Z',
|
||||
'executed_at' => '2021-05-14T12:29:27Z'
|
||||
},
|
||||
{
|
||||
'id' => 80_027_218_657,
|
||||
'billable' => true,
|
||||
'note' => 'Example Preparation 2',
|
||||
'timer_running' => false,
|
||||
'agent_id' => 80_014_400_475,
|
||||
'ticket_id' => 1001,
|
||||
'time_spent' => '02:20',
|
||||
'created_at' => '2021-05-15T12:29:27Z',
|
||||
'updated_at' => '2021-05-15T12:29:27Z',
|
||||
'start_time' => '2021-05-15T12:29:27Z',
|
||||
'executed_at' => '2021-05-15T12:29:27Z'
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
let(:imported_time_entry) do
|
||||
{
|
||||
ticket_id: ticket.id,
|
||||
created_by_id: 1,
|
||||
time_unit: 140,
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
# Mock the groups get request
|
||||
stub_request(:get, 'https://yours.freshdesk.com/api/v2/tickets/1001/time_entries?per_page=100').to_return(status: 200, body: JSON.generate(resources_payloud), headers: {})
|
||||
end
|
||||
|
||||
it 'add time entry for ticket' do
|
||||
expect { process(process_payload) }.to change(Ticket::TimeAccounting, :count).by(2)
|
||||
end
|
||||
|
||||
it 'check last time unit for ticket' do
|
||||
process(process_payload)
|
||||
expect(Ticket::TimeAccounting.last).to have_attributes(imported_time_entry)
|
||||
end
|
||||
|
||||
context 'with empty time entries' do
|
||||
let(:resources_payloud) { [] }
|
||||
|
||||
it 'do not change time entry for ticket' do
|
||||
expect { process(process_payload) }.to change(Ticket::TimeAccounting, :count).by(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when time entry feature is not available' do
|
||||
let(:time_entry_available) { false }
|
||||
|
||||
it 'add time entry for ticket' do
|
||||
expect { process(process_payload) }.to change(Ticket::TimeAccounting, :count).by(0)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@ RSpec.describe ::Sequencer::Sequence::Import::Freshdesk::TimeEntry, sequencer: :
|
|||
{
|
||||
'id' => 80_027_218_656,
|
||||
'billable' => true,
|
||||
'note' => 'Example Prepartion',
|
||||
'note' => 'Example Preparation',
|
||||
'timer_running' => false,
|
||||
'agent_id' => 80_014_400_475,
|
||||
'ticket_id' => 1001,
|
||||
|
@ -57,6 +57,7 @@ RSpec.describe ::Sequencer::Sequence::Import::Freshdesk::TimeEntry, sequencer: :
|
|||
|
||||
it 'correct attributes for added time entry' do
|
||||
process(process_payload)
|
||||
Rails.logger.debug Ticket::TimeAccounting.last
|
||||
expect(Ticket::TimeAccounting.last).to have_attributes(imported_time_entry)
|
||||
end
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ RSpec.describe ::Sequencer::Unit::Import::Freshdesk::Tickets, sequencer: :unit,
|
|||
request_params: {},
|
||||
field_map: {},
|
||||
id_map: id_map,
|
||||
time_entry_available: true,
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Sequencer::Unit::Import::Freshdesk::TimeEntry::Available, sequencer: :unit do
|
||||
|
||||
context 'when checking the availability of the time entry feature' do
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
dry_run: false,
|
||||
import_job: instance_double(ImportJob),
|
||||
field_map: {},
|
||||
id_map: {},
|
||||
}
|
||||
end
|
||||
|
||||
let(:response_ok) { Net::HTTPOK.new(1.0, '200', 'OK') }
|
||||
let(:response_forbidden) { Net::HTTPUnauthorized.new(1.0, '403', 'Forbidden') }
|
||||
|
||||
it 'check for avilable time entry feature' do
|
||||
allow(described_class).to receive(:perform_request).with(any_args).and_return(response_ok)
|
||||
expect(process(params)).to eq({ time_entry_available: true })
|
||||
end
|
||||
|
||||
it 'check for not available time entry feature' do
|
||||
allow(described_class).to receive(:perform_request).with(any_args).and_return(response_forbidden)
|
||||
expect(process(params)).to eq({ time_entry_available: false })
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue