fix(inboxes): preserve disabled inbox status updates
This commit is contained in:
@@ -2,7 +2,7 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController
|
||||
include Events::Types
|
||||
|
||||
DISABLED_INBOX_ACTIONS = [
|
||||
:create, :toggle_typing, :toggle_status, :update_last_seen, :set_custom_attributes, :destroy_custom_attributes, :transcript
|
||||
:create, :toggle_typing, :toggle_status, :set_custom_attributes, :destroy_custom_attributes, :transcript
|
||||
].freeze
|
||||
|
||||
before_action :render_not_found_if_empty, only: [:toggle_typing, :toggle_status, :set_custom_attributes, :destroy_custom_attributes]
|
||||
|
||||
@@ -19,9 +19,8 @@ class SendReplyJob < ApplicationJob
|
||||
|
||||
def perform(message_id)
|
||||
message = Message.find(message_id)
|
||||
return mark_message_failed(message) unless message.inbox.active?
|
||||
|
||||
channel_name = message.conversation.inbox.channel.class.to_s
|
||||
return handle_disabled_inbox(message, channel_name) unless message.inbox.active?
|
||||
|
||||
return send_on_facebook_page(message) if channel_name == 'Channel::FacebookPage'
|
||||
|
||||
@@ -33,6 +32,31 @@ class SendReplyJob < ApplicationJob
|
||||
|
||||
private
|
||||
|
||||
def handle_disabled_inbox(message, channel_name)
|
||||
return unless send_service_available?(channel_name)
|
||||
return unless deliverable_reply?(message, channel_name)
|
||||
|
||||
mark_message_failed(message)
|
||||
end
|
||||
|
||||
def send_service_available?(channel_name)
|
||||
channel_name == 'Channel::FacebookPage' || CHANNEL_SERVICES.key?(channel_name)
|
||||
end
|
||||
|
||||
def deliverable_reply?(message, channel_name)
|
||||
return false if message.private?
|
||||
return false unless message.outgoing? || message.template?
|
||||
return false if message.source_id.present?
|
||||
return false if message.content_type == 'voice_call'
|
||||
return message.email_notifiable_message? if email_delivery_channel?(channel_name)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def email_delivery_channel?(channel_name)
|
||||
%w[Channel::Email Channel::WebWidget Channel::Api].include?(channel_name)
|
||||
end
|
||||
|
||||
def mark_message_failed(message)
|
||||
Messages::StatusUpdateService.new(message, 'failed', INBOX_DISABLED_ERROR).perform
|
||||
end
|
||||
|
||||
@@ -129,7 +129,7 @@ class Webhooks::WhatsappEventsJob < MutexApplicationJob
|
||||
# Only skip for embedded signup when reauth is required; manual flow uses API keys and should still receive webhooks
|
||||
return true if channel.reauthorization_required? && embedded_signup_channel?(channel)
|
||||
return true unless channel.account.active?
|
||||
return true unless channel.inbox.active? || status_update_event?(params)
|
||||
return true unless channel.inbox.active? || existing_message_update_event?(params)
|
||||
|
||||
false
|
||||
end
|
||||
@@ -139,6 +139,14 @@ class Webhooks::WhatsappEventsJob < MutexApplicationJob
|
||||
value[:statuses].present?
|
||||
end
|
||||
|
||||
def existing_message_update_event?(params)
|
||||
status_update_event?(params) || call_event?(params)
|
||||
end
|
||||
|
||||
def call_event?(params)
|
||||
params.dig(:entry, 0, :changes, 0, :field) == 'calls'
|
||||
end
|
||||
|
||||
def embedded_signup_channel?(channel)
|
||||
(channel.provider_config || {}).to_h['source'] == 'embedded_signup'
|
||||
end
|
||||
|
||||
@@ -75,7 +75,7 @@ class Whatsapp::IncomingCallService
|
||||
end
|
||||
|
||||
def create_inbound_call(payload)
|
||||
unless inbox.channel.inbound_calls_enabled?
|
||||
unless inbox.active? && inbox.channel.inbound_calls_enabled?
|
||||
Rails.logger.info "[WHATSAPP CALL] Inbound calls disabled for inbox #{inbox.id}; rejecting call #{payload[:id]}"
|
||||
inbox.channel.provider_service.reject_call(payload[:id])
|
||||
return
|
||||
|
||||
@@ -269,6 +269,22 @@ RSpec.describe '/api/v1/widget/conversations/toggle_typing', type: :request do
|
||||
|
||||
expect(conversation.reload.contact_last_seen_at).not_to be_nil
|
||||
end
|
||||
|
||||
it 'updates last seen when the inbox is disabled' do
|
||||
current_time = DateTime.now.utc
|
||||
allow(DateTime).to receive(:now).and_return(current_time)
|
||||
web_widget.inbox.update!(active: false)
|
||||
|
||||
expect(Conversations::UpdateMessageStatusJob).to receive(:perform_later).with(conversation.id, current_time)
|
||||
|
||||
post '/api/v1/widget/conversations/update_last_seen',
|
||||
headers: { 'X-Auth-Token' => token },
|
||||
params: { website_token: web_widget.website_token },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(conversation.reload.contact_last_seen_at).not_to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -44,6 +44,19 @@ describe Whatsapp::IncomingCallService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the inbox is disabled' do
|
||||
it 'rejects the call with Meta without creating a Call or Conversation' do
|
||||
inbox.update!(active: false)
|
||||
provider_service = instance_double(Whatsapp::Providers::WhatsappCloudService, reject_call: true)
|
||||
allow(inbox.channel).to receive(:provider_service).and_return(provider_service)
|
||||
|
||||
params = call_payload(event: 'connect', session: { sdp: "v=0\r\n...sdp...", sdp_type: 'offer' })
|
||||
expect { described_class.new(inbox: inbox, params: params).perform }
|
||||
.to not_change(Call, :count).and not_change(Conversation, :count)
|
||||
expect(provider_service).to have_received(:reject_call).with(provider_call_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'inbound connect' do
|
||||
let(:sdp_offer) { "v=0\r\n...sdp..." }
|
||||
let!(:agent) { create(:user, account: account) }
|
||||
|
||||
@@ -125,7 +125,7 @@ RSpec.describe SendReplyJob do
|
||||
|
||||
it 'marks the message as failed when the inbox is disabled before the queued send runs' do
|
||||
twilio_channel = create(:channel_twilio_sms)
|
||||
message = create(:message, conversation: create(:conversation, inbox: twilio_channel.inbox))
|
||||
message = create(:message, message_type: :outgoing, conversation: create(:conversation, inbox: twilio_channel.inbox))
|
||||
twilio_channel.inbox.update!(active: false)
|
||||
|
||||
expect(Twilio::SendOnTwilioService).not_to receive(:new)
|
||||
@@ -135,5 +135,38 @@ RSpec.describe SendReplyJob do
|
||||
expect(message.reload).to be_failed
|
||||
expect(message.external_error).to eq('This inbox is currently disabled')
|
||||
end
|
||||
|
||||
it 'does not mark incoming messages as failed when the inbox is disabled' do
|
||||
twilio_channel = create(:channel_twilio_sms)
|
||||
message = create(:message, message_type: :incoming, conversation: create(:conversation, inbox: twilio_channel.inbox))
|
||||
twilio_channel.inbox.update!(active: false)
|
||||
|
||||
described_class.perform_now(message.id)
|
||||
|
||||
expect(message.reload).not_to be_failed
|
||||
expect(message.external_error).to be_nil
|
||||
end
|
||||
|
||||
it 'does not mark private notes as failed when the inbox is disabled' do
|
||||
twilio_channel = create(:channel_twilio_sms)
|
||||
message = create(:message, private: true, conversation: create(:conversation, inbox: twilio_channel.inbox))
|
||||
twilio_channel.inbox.update!(active: false)
|
||||
|
||||
described_class.perform_now(message.id)
|
||||
|
||||
expect(message.reload).not_to be_failed
|
||||
expect(message.external_error).to be_nil
|
||||
end
|
||||
|
||||
it 'does not mark provider echo messages as failed when the inbox is disabled' do
|
||||
twilio_channel = create(:channel_twilio_sms)
|
||||
message = create(:message, source_id: 'provider-message-id', conversation: create(:conversation, inbox: twilio_channel.inbox))
|
||||
twilio_channel.inbox.update!(active: false)
|
||||
|
||||
described_class.perform_now(message.id)
|
||||
|
||||
expect(message.reload).not_to be_failed
|
||||
expect(message.external_error).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -134,6 +134,27 @@ RSpec.describe Webhooks::WhatsappEventsJob do
|
||||
job.perform_now(wb_params)
|
||||
end
|
||||
|
||||
it 'processes call callbacks when the inbox is disabled' do
|
||||
wb_params = params.deep_dup
|
||||
wb_params[:entry].first[:changes].first[:field] = 'calls'
|
||||
wb_params[:entry].first[:changes].first[:value][:calls] = [
|
||||
{ id: 'wacid-test', from: '919745786257', event: 'connect', session: { sdp_type: 'offer' } }
|
||||
]
|
||||
channel.inbox.update!(active: false)
|
||||
|
||||
allow(Whatsapp::IncomingCallService).to receive(:new).and_return(process_service)
|
||||
|
||||
expect(Whatsapp::IncomingCallService).to receive(:new).with(
|
||||
inbox: channel.inbox,
|
||||
params: {
|
||||
calls: [wb_params[:entry].first[:changes].first[:value][:calls].first],
|
||||
contacts: nil
|
||||
}
|
||||
)
|
||||
|
||||
job.perform_now(wb_params)
|
||||
end
|
||||
|
||||
it 'uses from_user_id as the mutex sender for BSUID-only inbound messages' do
|
||||
bsuid = 'IN.2081978709342942'
|
||||
wb_params = params.deep_dup
|
||||
|
||||
Reference in New Issue
Block a user