feat: allow non-admin to refresh baileys connection, and automatically setup connection provider on error (#89)
* feat: allow non-admin to refresh baileys connection, and automatically setup connection provider on error * Update app/javascript/dashboard/components/widgets/conversation/MessagesView.vue Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore: ignore console lint * feat: handle disconnect errors gracefully and update tests accordingly --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
adee31a383
commit
11f8aac294
@ -5,6 +5,8 @@ import { useConfig } from 'dashboard/composables/useConfig';
|
||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||
import { useAI } from 'dashboard/composables/useAI';
|
||||
import { useAdmin } from 'dashboard/composables/useAdmin';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
// components
|
||||
import ReplyBox from './ReplyBox.vue';
|
||||
@ -53,6 +55,7 @@ export default {
|
||||
const isPopOutReplyBox = ref(false);
|
||||
const conversationPanelRef = ref(null);
|
||||
const { isEnterprise } = useConfig();
|
||||
const store = useStore();
|
||||
|
||||
const keyboardEvents = {
|
||||
Escape: {
|
||||
@ -82,6 +85,7 @@ export default {
|
||||
fetchLabelSuggestions,
|
||||
conversationPanelRef,
|
||||
isAdmin,
|
||||
store,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
@ -467,6 +471,15 @@ export default {
|
||||
onCloseBaileysLinkDeviceModal() {
|
||||
this.showBaileysLinkDeviceModal = false;
|
||||
},
|
||||
onSetupProviderConnection() {
|
||||
this.store
|
||||
.dispatch('inboxes/setupChannelProvider', this.inbox.id)
|
||||
.catch(e => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error setting up provider connection:', e);
|
||||
useAlert('Failed to reconnect. Please try again or contact support.');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -493,14 +506,18 @@ export default {
|
||||
'CONVERSATION.INBOX.WHATSAPP_BAILEYS_PROVIDER_CONNECTION.NOT_CONNECTED_CONTACT_ADMIN'
|
||||
)
|
||||
"
|
||||
:has-action-button="isAdmin"
|
||||
has-action-button
|
||||
:action-button-label="
|
||||
$t(
|
||||
'CONVERSATION.INBOX.WHATSAPP_BAILEYS_PROVIDER_CONNECTION.LINK_DEVICE'
|
||||
)
|
||||
isAdmin
|
||||
? $t(
|
||||
'CONVERSATION.INBOX.WHATSAPP_BAILEYS_PROVIDER_CONNECTION.LINK_DEVICE'
|
||||
)
|
||||
: ''
|
||||
"
|
||||
:action-button-icon="isAdmin ? '' : 'i-lucide-refresh-cw'"
|
||||
@primary-action="
|
||||
isAdmin ? onOpenBaileysLinkDeviceModal() : onSetupProviderConnection()
|
||||
"
|
||||
action-button-icon=""
|
||||
@primary-action="onOpenBaileysLinkDeviceModal"
|
||||
/>
|
||||
</template>
|
||||
<Banner
|
||||
|
||||
@ -254,7 +254,7 @@
|
||||
"INBOX": {
|
||||
"WHATSAPP_BAILEYS_PROVIDER_CONNECTION": {
|
||||
"NOT_CONNECTED": "WhatsApp is not connected. Please link your device again.",
|
||||
"NOT_CONNECTED_CONTACT_ADMIN": "WhatsApp is not connected. Please contact your administrator to link your device again.",
|
||||
"NOT_CONNECTED_CONTACT_ADMIN": "WhatsApp is not connected. Click this button to try to reconnect, or please contact your administrator to link your device again.",
|
||||
"LINK_DEVICE": "Link device"
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@
|
||||
"INBOX": {
|
||||
"WHATSAPP_BAILEYS_PROVIDER_CONNECTION": {
|
||||
"NOT_CONNECTED": "O WhatsApp não está conectado. Por favor conecte o seu dispositivo novamente.",
|
||||
"NOT_CONNECTED_CONTACT_ADMIN": "O WhatsApp não está conectado. Por favor contate o seu administrador para conectar o dispositivo novamente.",
|
||||
"NOT_CONNECTED_CONTACT_ADMIN": "O WhatsApp não está conectado. Clique no botão ao lado para tentar reconectar, ou contate o seu administrador para conectar o dispositivo novamente.",
|
||||
"LINK_DEVICE": "Conectar dispositivo"
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,6 +111,9 @@ class Channel::Whatsapp < ApplicationRecord
|
||||
|
||||
def disconnect_channel_provider
|
||||
provider_service.disconnect_channel_provider
|
||||
rescue StandardError => e
|
||||
# NOTE: Don't prevent destruction if disconnect fails
|
||||
Rails.logger.error "Failed to disconnect channel provider: #{e.message}"
|
||||
end
|
||||
|
||||
def received_messages(messages, conversation)
|
||||
|
||||
@ -2,7 +2,7 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
include BaileysHelper
|
||||
|
||||
class MessageContentTypeNotSupported < StandardError; end
|
||||
class MessageNotSentError < StandardError; end
|
||||
class ProviderUnavailableError < StandardError; end
|
||||
|
||||
DEFAULT_CLIENT_NAME = ENV.fetch('BAILEYS_PROVIDER_DEFAULT_CLIENT_NAME', nil)
|
||||
DEFAULT_URL = ENV.fetch('BAILEYS_PROVIDER_DEFAULT_URL', nil)
|
||||
@ -21,7 +21,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
}.compact.to_json
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def disconnect_channel_provider
|
||||
@ -30,7 +32,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
headers: api_headers
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def send_message(phone_number, message)
|
||||
@ -89,7 +93,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
}.to_json
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def update_presence(status)
|
||||
@ -107,7 +113,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
}.to_json
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def read_messages(phone_number, messages)
|
||||
@ -127,7 +135,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
}.to_json
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def unread_message(phone_number, message) # rubocop:disable Metrics/MethodLength
|
||||
@ -152,7 +162,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
}.to_json
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def received_messages(phone_number, messages)
|
||||
@ -172,7 +184,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
}.to_json
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
@ -230,7 +244,7 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
}.to_json
|
||||
)
|
||||
|
||||
raise MessageNotSentError unless process_response(response)
|
||||
raise ProviderUnavailableError unless process_response(response)
|
||||
|
||||
update_external_created_at(response)
|
||||
response.parsed_response.dig('data', 'key', 'id')
|
||||
@ -257,6 +271,10 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
method_names.each do |method_name|
|
||||
original_method = instance_method(method_name)
|
||||
|
||||
define_method("#{method_name}_without_error_handling") do |*args, &block|
|
||||
original_method.bind_call(self, *args, &block)
|
||||
end
|
||||
|
||||
define_method(method_name) do |*args, &block|
|
||||
original_method.bind_call(self, *args, &block)
|
||||
rescue StandardError => e
|
||||
@ -268,6 +286,17 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
|
||||
def handle_channel_error
|
||||
whatsapp_channel.update_provider_connection!(connection: 'close')
|
||||
|
||||
return if @handling_error
|
||||
|
||||
@handling_error = true
|
||||
begin
|
||||
setup_channel_provider_without_error_handling
|
||||
rescue StandardError => e
|
||||
Rails.logger.error "Failed to reconnect channel after error: #{e.message}"
|
||||
ensure
|
||||
@handling_error = false
|
||||
end
|
||||
end
|
||||
|
||||
with_error_handling :setup_channel_provider,
|
||||
|
||||
@ -311,6 +311,8 @@ RSpec.describe Channel::Whatsapp do
|
||||
|
||||
it 'destroys the channel on failure to disconnect' do
|
||||
stub_request(:delete, disconnect_url).to_return(status: 404, body: 'error message')
|
||||
# NOTE: On failure, `setup_channel_provider` is called, so we re-stub to avoid errors
|
||||
stub_request(:post, disconnect_url).to_return(status: 200)
|
||||
|
||||
channel.destroy!
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
end
|
||||
|
||||
context 'when response is unsuccessful' do
|
||||
it 'logs the error and returns false' do
|
||||
it 'raises ProviderUnavailableError and logs the error' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
@ -51,12 +51,14 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
body: 'error message',
|
||||
headers: {}
|
||||
)
|
||||
allow(Rails.logger).to receive(:error).with('error message')
|
||||
|
||||
response = service.setup_channel_provider
|
||||
allow(Rails.logger).to receive(:error)
|
||||
|
||||
expect(response).to be(false)
|
||||
expect(Rails.logger).to have_received(:error)
|
||||
expect do
|
||||
service.setup_channel_provider
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
|
||||
expect(Rails.logger).to have_received(:error).with('error message').twice
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -75,7 +77,8 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
end
|
||||
|
||||
context 'when response is unsuccessful' do
|
||||
it 'logs the error and returns false' do
|
||||
it 'raises ProviderUnavailableError and logs the error' do
|
||||
# Stub the failing request
|
||||
stub_request(:delete, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.with(headers: stub_headers(whatsapp_channel))
|
||||
.to_return(
|
||||
@ -83,12 +86,18 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
body: 'error message',
|
||||
headers: {}
|
||||
)
|
||||
allow(Rails.logger).to receive(:error).with('error message')
|
||||
|
||||
response = service.disconnect_channel_provider
|
||||
# Stub the reconnection attempt
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
expect(response).to be(false)
|
||||
expect(Rails.logger).to have_received(:error)
|
||||
allow(Rails.logger).to receive(:error)
|
||||
|
||||
expect do
|
||||
service.disconnect_channel_provider
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
|
||||
expect(Rails.logger).to have_received(:error).with('error message')
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -325,7 +334,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
end
|
||||
|
||||
context 'when request is unsuccessful' do
|
||||
it 'raises MessageNotSentError' do
|
||||
it 'raises ProviderUnavailableError' do
|
||||
stub_request(:post, request_path)
|
||||
.to_return(
|
||||
status: 400,
|
||||
@ -333,9 +342,13 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
body: result_body.to_json
|
||||
)
|
||||
|
||||
# Stub the reconnection attempt
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
expect do
|
||||
service.send_message(test_send_phone_number, message)
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::MessageNotSentError)
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -376,26 +389,6 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
expect(service.validate_provider_config?).to be(false)
|
||||
expect(Rails.logger).to have_received(:error)
|
||||
end
|
||||
|
||||
context 'when provider responds with 5XX' do
|
||||
it 'updated provider connection to close' do
|
||||
whatsapp_channel.update!(provider_connection: { 'connection' => 'open' })
|
||||
allow(HTTParty).to receive(:post).with(
|
||||
"#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message",
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
jid: test_send_jid,
|
||||
messageContent: { text: message.content }
|
||||
}.to_json
|
||||
).and_raise(HTTParty::ResponseError.new(OpenStruct.new(status_code: 500)))
|
||||
|
||||
expect do
|
||||
service.send_message(test_send_phone_number, message)
|
||||
end.to raise_error(HTTParty::ResponseError)
|
||||
|
||||
expect(whatsapp_channel.provider_connection['connection']).to eq('close')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -411,6 +404,24 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
|
||||
expect(result).to be(true)
|
||||
end
|
||||
|
||||
context 'when request is unsuccessful' do
|
||||
it 'raises ProviderUnavailableError' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/read-messages")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: { keys: [{ id: message.source_id, remoteJid: test_send_jid, fromMe: false }] }.to_json
|
||||
).to_return(status: 400, body: 'error message', headers: {})
|
||||
|
||||
# Stub the reconnection attempt
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
expect do
|
||||
service.read_messages(test_send_phone_number, [message])
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#unread_message' do
|
||||
@ -436,6 +447,35 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
|
||||
expect(result).to be(true)
|
||||
end
|
||||
|
||||
context 'when request is unsuccessful' do
|
||||
it 'raises ProviderUnavailableError' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/chat-modify")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
jid: test_send_jid,
|
||||
mod: {
|
||||
markRead: false,
|
||||
lastMessages: [
|
||||
{
|
||||
key: { id: 'msg_123', remoteJid: test_send_jid, fromMe: false },
|
||||
messageTimestamp: 123
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json
|
||||
).to_return(status: 400, body: 'error message', headers: {})
|
||||
|
||||
# Stub the reconnection attempt
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
expect do
|
||||
service.unread_message(test_send_phone_number, message)
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#received_messages' do
|
||||
@ -452,6 +492,26 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
|
||||
expect(result).to be(true)
|
||||
end
|
||||
|
||||
context 'when request is unsuccessful' do
|
||||
it 'raises ProviderUnavailableError' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-receipts")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
keys: [{ id: message.source_id, remoteJid: test_send_jid, fromMe: false }]
|
||||
}.to_json
|
||||
).to_return(status: 400, body: 'error message', headers: {})
|
||||
|
||||
# Stub the reconnection attempt
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
expect do
|
||||
service.received_messages(test_send_phone_number, [message])
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#toggle_typing_status' do
|
||||
@ -505,26 +565,33 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
expect(request).to have_been_requested
|
||||
end
|
||||
|
||||
it 'logs the error and returns false' do
|
||||
stub_request(:patch, request_path)
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
toJid: test_send_jid,
|
||||
type: 'composing'
|
||||
}.to_json
|
||||
)
|
||||
.to_return(
|
||||
status: 400,
|
||||
body: 'error message',
|
||||
headers: {}
|
||||
)
|
||||
allow(Rails.logger).to receive(:error).with('error message')
|
||||
context 'when request is unsuccessful' do
|
||||
it 'raises ProviderUnavailableError and logs the error' do
|
||||
stub_request(:patch, request_path)
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
toJid: test_send_jid,
|
||||
type: 'composing'
|
||||
}.to_json
|
||||
)
|
||||
.to_return(
|
||||
status: 400,
|
||||
body: 'error message',
|
||||
headers: {}
|
||||
)
|
||||
|
||||
response = service.toggle_typing_status(test_send_phone_number, Events::Types::CONVERSATION_TYPING_ON)
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
expect(response).to be(false)
|
||||
expect(Rails.logger).to have_received(:error)
|
||||
allow(Rails.logger).to receive(:error)
|
||||
|
||||
expect do
|
||||
service.toggle_typing_status(test_send_phone_number, Events::Types::CONVERSATION_TYPING_ON)
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
|
||||
expect(Rails.logger).to have_received(:error).with('error message')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -546,25 +613,32 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
expect(request).to have_been_requested
|
||||
end
|
||||
|
||||
it 'logs the error and returns false' do
|
||||
stub_request(:patch, request_path)
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
type: 'available'
|
||||
}.to_json
|
||||
)
|
||||
.to_return(
|
||||
status: 400,
|
||||
body: 'error message',
|
||||
headers: {}
|
||||
)
|
||||
allow(Rails.logger).to receive(:error).with('error message')
|
||||
context 'when request is unsuccessful' do
|
||||
it 'raises ProviderUnavailableError and logs the error' do
|
||||
stub_request(:patch, request_path)
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
type: 'available'
|
||||
}.to_json
|
||||
)
|
||||
.to_return(
|
||||
status: 400,
|
||||
body: 'error message',
|
||||
headers: {}
|
||||
)
|
||||
|
||||
response = service.update_presence('online')
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
expect(response).to be(false)
|
||||
expect(Rails.logger).to have_received(:error)
|
||||
allow(Rails.logger).to receive(:error)
|
||||
|
||||
expect do
|
||||
service.update_presence('online')
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
|
||||
expect(Rails.logger).to have_received(:error).with('error message')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -586,6 +660,138 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'error handling' do
|
||||
describe '#handle_channel_error' do
|
||||
it 'updates provider connection to close' do
|
||||
whatsapp_channel.update!(provider_connection: { 'connection' => 'open' })
|
||||
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
clientName: 'chatwoot-test',
|
||||
webhookUrl: whatsapp_channel.inbox.callback_webhook_url,
|
||||
webhookVerifyToken: whatsapp_channel.provider_config['webhook_verify_token'],
|
||||
includeMedia: false
|
||||
}.to_json
|
||||
)
|
||||
.to_return(status: 200)
|
||||
|
||||
service.send(:handle_channel_error)
|
||||
|
||||
expect(whatsapp_channel.reload.provider_connection['connection']).to eq('close')
|
||||
end
|
||||
|
||||
it 'attempts to reconnect by calling setup_channel_provider' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
clientName: 'chatwoot-test',
|
||||
webhookUrl: whatsapp_channel.inbox.callback_webhook_url,
|
||||
webhookVerifyToken: whatsapp_channel.provider_config['webhook_verify_token'],
|
||||
includeMedia: false
|
||||
}.to_json
|
||||
)
|
||||
.to_return(status: 200)
|
||||
|
||||
service.send(:handle_channel_error)
|
||||
|
||||
expect(WebMock).to have_requested(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
end
|
||||
|
||||
it 'logs error and does not raise when reconnection fails' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
clientName: 'chatwoot-test',
|
||||
webhookUrl: whatsapp_channel.inbox.callback_webhook_url,
|
||||
webhookVerifyToken: whatsapp_channel.provider_config['webhook_verify_token'],
|
||||
includeMedia: false
|
||||
}.to_json
|
||||
)
|
||||
.to_return(status: 400, body: 'reconnection failed')
|
||||
|
||||
allow(Rails.logger).to receive(:error)
|
||||
|
||||
expect { service.send(:handle_channel_error) }.not_to raise_error
|
||||
|
||||
expect(Rails.logger).to have_received(:error).with(/Failed to reconnect channel after error/)
|
||||
end
|
||||
|
||||
it 'prevents infinite loop with @handling_error flag' do
|
||||
service.instance_variable_set(:@handling_error, true)
|
||||
|
||||
expect(HTTParty).not_to receive(:post)
|
||||
|
||||
service.send(:handle_channel_error)
|
||||
|
||||
expect(whatsapp_channel.reload.provider_connection['connection']).to eq('close')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'error handling wrapper' do
|
||||
context 'when send_message fails' do
|
||||
it 'calls handle_channel_error and re-raises the error' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
||||
.to_return(status: 500, body: 'server error')
|
||||
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
whatsapp_channel.update!(provider_connection: { 'connection' => 'open' })
|
||||
|
||||
expect do
|
||||
service.send_message(test_send_phone_number, message)
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
|
||||
expect(whatsapp_channel.reload.provider_connection['connection']).to eq('close')
|
||||
|
||||
expect(WebMock).to have_requested(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when setup_channel_provider fails' do
|
||||
it 'calls handle_channel_error and re-raises the error' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 500, body: 'server error')
|
||||
|
||||
whatsapp_channel.update!(provider_connection: { 'connection' => 'open' })
|
||||
allow(Rails.logger).to receive(:error)
|
||||
|
||||
expect do
|
||||
service.setup_channel_provider
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
|
||||
expect(whatsapp_channel.reload.provider_connection['connection']).to eq('close')
|
||||
|
||||
expect(Rails.logger).to have_received(:error).with(/Failed to reconnect channel after error/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when toggle_typing_status fails' do
|
||||
it 'calls handle_channel_error and re-raises the error' do
|
||||
stub_request(:patch, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/presence")
|
||||
.to_return(status: 500, body: 'server error')
|
||||
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
.to_return(status: 200)
|
||||
|
||||
whatsapp_channel.update!(provider_connection: { 'connection' => 'open' })
|
||||
|
||||
expect do
|
||||
service.toggle_typing_status(test_send_phone_number, Events::Types::CONVERSATION_TYPING_ON)
|
||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||
|
||||
expect(whatsapp_channel.reload.provider_connection['connection']).to eq('close')
|
||||
|
||||
expect(WebMock).to have_requested(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def stub_headers(channel)
|
||||
{
|
||||
'Content-Type' => 'application/json',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user