fix(voice): close lock-window gaps in accept, throttle, and call_attributes_changed?

This commit is contained in:
Tanmay Deep Sharma
2026-04-30 18:46:09 +07:00
parent cbef2d4e66
commit 0618e6e229
3 changed files with 31 additions and 10 deletions
@@ -113,14 +113,28 @@ class Api::V1::Accounts::WhatsappCallsController < Api::V1::Accounts::BaseContro
end
# 138006 = no call permission yet; send opt-in template (throttled) and surface state to FE.
# Lock the conversation so concurrent initiate requests can't both pass the throttle gate
# and double-send the opt-in template.
def render_permission_request
return render json: { status: 'permission_pending' } if permission_request_throttled?
status = nil
@conversation.with_lock do
if permission_request_throttled?
status = 'permission_pending'
next
end
sent = provider_service.send_call_permission_request(@conversation.contact.phone_number.delete('+'))
return render_could_not_create_error(I18n.t('errors.whatsapp.calls.permission_request_failed')) unless sent
sent = provider_service.send_call_permission_request(@conversation.contact.phone_number.delete('+'))
if sent
record_permission_request_wamid(sent)
status = 'permission_requested'
else
status = 'failed'
end
end
record_permission_request_wamid(sent)
render json: { status: 'permission_requested' }
return render_could_not_create_error(I18n.t('errors.whatsapp.calls.permission_request_failed')) if status == 'failed'
render json: { status: status }
end
def permission_request_throttled?
@@ -40,6 +40,9 @@ module Enterprise::Conversation
def call_attributes_changed?
return false if previous_changes['additional_attributes'].blank?
previous_changes['additional_attributes'][1].keys.intersect?(%w[call_status call_direction])
# Compare before/after values for call keys — checking key presence alone
# rebroadcasts on any unrelated additional_attributes write once the keys exist.
before, after = previous_changes['additional_attributes']
%w[call_status call_direction].any? { |key| (before || {})[key] != (after || {})[key] }
end
end
@@ -4,10 +4,14 @@ class Whatsapp::CallService
def accept
raise Voice::CallErrors::CallFailed, 'sdp_answer is required' if sdp_answer.blank?
call.with_lock { transition_to_in_progress! }
update_message_status('in_progress')
update_conversation_call_status(call.display_status)
broadcast(:accepted, accepted_by_agent_id: agent.id)
# All side effects under the lock so a concurrent terminate cannot finalize
# the call between status update and the message/conversation/broadcast writes.
call.with_lock do
transition_to_in_progress!
update_message_status('in_progress')
update_conversation_call_status(call.display_status)
broadcast(:accepted, accepted_by_agent_id: agent.id)
end
call
end