diff --git a/app/jobs/agent_bots/webhook_job.rb b/app/jobs/agent_bots/webhook_job.rb index d0c4e7959..b3a3d6cc1 100644 --- a/app/jobs/agent_bots/webhook_job.rb +++ b/app/jobs/agent_bots/webhook_job.rb @@ -1,3 +1,7 @@ class AgentBots::WebhookJob < WebhookJob queue_as :high + + def perform(url, payload, webhook_type = :agent_bot_webhook) + super(url, payload, webhook_type) + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 6afab9253..ad54b8dfa 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -202,6 +202,8 @@ en: captain: resolved: 'Conversation was marked resolved by %{user_name} due to inactivity' open: 'Conversation was marked open by %{user_name}' + agent_bot: + error_moved_to_open: 'Conversation was marked open by system due to an error with the agent bot.' status: resolved: 'Conversation was marked resolved by %{user_name}' contact_resolved: 'Conversation was resolved by %{contact_name}' diff --git a/lib/webhooks/trigger.rb b/lib/webhooks/trigger.rb index 41b3a415d..95c399d54 100644 --- a/lib/webhooks/trigger.rb +++ b/lib/webhooks/trigger.rb @@ -31,14 +31,33 @@ class Webhooks::Trigger end def handle_error(error) - return unless should_handle_error? + return unless SUPPORTED_ERROR_HANDLE_EVENTS.include?(@payload[:event]) return unless message - update_message_status(error) + case @webhook_type + when :agent_bot_webhook + conversation = message.conversation + return unless conversation&.pending? + + conversation.open! + create_agent_bot_error_activity(conversation) + when :api_inbox_webhook + update_message_status(error) + end end - def should_handle_error? - @webhook_type == :api_inbox_webhook && SUPPORTED_ERROR_HANDLE_EVENTS.include?(@payload[:event]) + def create_agent_bot_error_activity(conversation) + content = I18n.t('conversations.activity.agent_bot.error_moved_to_open') + Conversations::ActivityMessageJob.perform_later(conversation, activity_message_params(conversation, content)) + end + + def activity_message_params(conversation, content) + { + account_id: conversation.account_id, + inbox_id: conversation.inbox_id, + message_type: :activity, + content: content + } end def update_message_status(error) diff --git a/spec/lib/webhooks/trigger_spec.rb b/spec/lib/webhooks/trigger_spec.rb index 8ff2a21a5..224a35e07 100644 --- a/spec/lib/webhooks/trigger_spec.rb +++ b/spec/lib/webhooks/trigger_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' describe Webhooks::Trigger do + include ActiveJob::TestHelper + subject(:trigger) { described_class } let!(:account) { create(:account) } @@ -8,8 +10,18 @@ describe Webhooks::Trigger do let!(:conversation) { create(:conversation, inbox: inbox) } let!(:message) { create(:message, account: account, inbox: inbox, conversation: conversation) } - let!(:webhook_type) { :api_inbox_webhook } + let(:webhook_type) { :api_inbox_webhook } let!(:url) { 'https://test.com' } + let(:agent_bot_error_content) { I18n.t('conversations.activity.agent_bot.error_moved_to_open') } + + before do + ActiveJob::Base.queue_adapter = :test + end + + after do + clear_enqueued_jobs + clear_performed_jobs + end describe '#execute' do it 'triggers webhook' do @@ -54,6 +66,57 @@ describe Webhooks::Trigger do ).and_raise(RestClient::ExceptionWithResponse.new('error', 500)).once expect { trigger.execute(url, payload, webhook_type) }.to change { message.reload.status }.from('sent').to('failed') end + + context 'when webhook type is agent bot' do + let(:webhook_type) { :agent_bot_webhook } + + it 'reopens conversation and enqueues activity message if pending' do + conversation.update(status: :pending) + payload = { event: 'message_created', conversation: { id: conversation.id }, id: message.id } + + expect(RestClient::Request).to receive(:execute) + .with( + method: :post, + url: url, + payload: payload.to_json, + headers: { content_type: :json, accept: :json }, + timeout: 5 + ).and_raise(RestClient::ExceptionWithResponse.new('error', 500)).once + + expect do + perform_enqueued_jobs do + trigger.execute(url, payload, webhook_type) + end + end.not_to(change { message.reload.status }) + + expect(conversation.reload.status).to eq('open') + + activity_message = conversation.reload.messages.order(:created_at).last + expect(activity_message.message_type).to eq('activity') + expect(activity_message.content).to eq(agent_bot_error_content) + end + + it 'does not change message status or enqueue activity when conversation is not pending' do + payload = { event: 'message_created', conversation: { id: conversation.id }, id: message.id } + + expect(RestClient::Request).to receive(:execute) + .with( + method: :post, + url: url, + payload: payload.to_json, + headers: { content_type: :json, accept: :json }, + timeout: 5 + ).and_raise(RestClient::ExceptionWithResponse.new('error', 500)).once + + expect do + trigger.execute(url, payload, webhook_type) + end.not_to(change { message.reload.status }) + + expect(Conversations::ActivityMessageJob).not_to have_been_enqueued + + expect(conversation.reload.status).to eq('open') + end + end end it 'does not update message status if webhook fails for other events' do