Merge branch 'develop' into feat/app-store-reviews
This commit is contained in:
@@ -126,12 +126,17 @@ class Webhooks::WhatsappEventsJob < MutexApplicationJob
|
||||
|
||||
def channel_is_inactive?(channel)
|
||||
return true if channel.blank?
|
||||
return true if channel.reauthorization_required?
|
||||
# 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?
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def embedded_signup_channel?(channel)
|
||||
(channel.provider_config || {}).to_h['source'] == 'embedded_signup'
|
||||
end
|
||||
|
||||
def find_channel_by_url_param(params)
|
||||
return unless params[:phone_number]
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class WebhookListener < BaseListener
|
||||
|
||||
def inbox_created(event)
|
||||
inbox, account = extract_inbox_and_account(event)
|
||||
inbox_webhook_data = Inbox::EventDataPresenter.new(inbox).push_data
|
||||
inbox_webhook_data = Inbox::EventDataPresenter.new(inbox).webhook_data
|
||||
payload = inbox_webhook_data.merge(event: __method__.to_s)
|
||||
deliver_account_webhooks(payload, account)
|
||||
end
|
||||
@@ -78,7 +78,7 @@ class WebhookListener < BaseListener
|
||||
changed_attributes = extract_changed_attributes(event)
|
||||
return if changed_attributes.blank?
|
||||
|
||||
inbox_webhook_data = Inbox::EventDataPresenter.new(inbox).push_data
|
||||
inbox_webhook_data = Inbox::EventDataPresenter.new(inbox).webhook_data
|
||||
payload = inbox_webhook_data.merge(event: __method__.to_s, changed_attributes: changed_attributes)
|
||||
deliver_account_webhooks(payload, account)
|
||||
end
|
||||
|
||||
@@ -23,7 +23,10 @@ class Conversations::EventDataPresenter < SimpleDelegator
|
||||
|
||||
# Like #push_data but with message text normalized for external integrations (webhooks).
|
||||
def webhook_data
|
||||
push_data.merge(messages: webhook_push_messages)
|
||||
push_data.merge(
|
||||
account: account.webhook_data,
|
||||
messages: webhook_push_messages
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -32,4 +32,8 @@ class Inbox::EventDataPresenter < SimpleDelegator
|
||||
channel: channel
|
||||
}
|
||||
end
|
||||
|
||||
def webhook_data
|
||||
push_data.merge(account: account.webhook_data)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -138,7 +138,11 @@ json.bot_name resource.channel.try(:bot_name) if resource.telegram?
|
||||
if resource.whatsapp?
|
||||
json.message_templates resource.channel.try(:message_templates)
|
||||
json.provider_config resource.channel.try(:provider_config) if Current.account_user&.administrator?
|
||||
json.reauthorization_required resource.channel.try(:reauthorization_required?)
|
||||
# Only show reauthorization for embedded signup; manual flow uses API keys, not OAuth
|
||||
json.reauthorization_required(
|
||||
(resource.channel.try(:provider_config) || {}).to_h['source'] == 'embedded_signup' &&
|
||||
resource.channel.try(:reauthorization_required?)
|
||||
)
|
||||
end
|
||||
|
||||
## Voice attributes for TwilioSms
|
||||
|
||||
@@ -100,6 +100,35 @@ RSpec.describe 'Inboxes API', type: :request do
|
||||
expect(JSON.parse(response.body, symbolize_names: true)[:id]).to eq(inbox.id)
|
||||
end
|
||||
|
||||
it 'returns reauthorization_required for embedded signup whatsapp channel when reauth required' do
|
||||
whatsapp_channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud', sync_templates: false,
|
||||
validate_provider_config: false)
|
||||
whatsapp_inbox = create(:inbox, channel: whatsapp_channel, account: account)
|
||||
whatsapp_channel.prompt_reauthorization!
|
||||
|
||||
get "/api/v1/accounts/#{account.id}/inboxes/#{whatsapp_inbox.id}",
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.parsed_body['reauthorization_required']).to be(true)
|
||||
end
|
||||
|
||||
it 'does not flag reauthorization_required for manual whatsapp channel even when reauth required' do
|
||||
whatsapp_channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud', sync_templates: false,
|
||||
validate_provider_config: false)
|
||||
whatsapp_channel.update!(provider_config: whatsapp_channel.provider_config.merge('source' => 'manual'))
|
||||
whatsapp_inbox = create(:inbox, channel: whatsapp_channel, account: account)
|
||||
whatsapp_channel.prompt_reauthorization!
|
||||
|
||||
get "/api/v1/accounts/#{account.id}/inboxes/#{whatsapp_inbox.id}",
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.parsed_body['reauthorization_required']).to be(false)
|
||||
end
|
||||
|
||||
it 'returns the inbox if assigned inbox is assigned as agent' do
|
||||
create(:inbox_member, user: agent, inbox: inbox)
|
||||
get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}",
|
||||
|
||||
@@ -62,6 +62,14 @@ RSpec.describe Webhooks::WhatsappEventsJob do
|
||||
job.perform_now(params)
|
||||
end
|
||||
|
||||
it 'still enqueues for manual channels even when reauthorization required' do
|
||||
channel.update!(provider_config: channel.provider_config.merge('source' => 'manual'))
|
||||
channel.prompt_reauthorization!
|
||||
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
||||
expect(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new)
|
||||
job.perform_now(params)
|
||||
end
|
||||
|
||||
it 'will not enqueue if channel is not present' do
|
||||
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
||||
allow(Whatsapp::IncomingMessageService).to receive(:new).and_return(process_service)
|
||||
|
||||
@@ -82,7 +82,7 @@ describe AgentBotListener do
|
||||
create(:agent_bot_inbox, inbox: inbox, agent_bot: agent_bot)
|
||||
expect(AgentBots::WebhookJob).to receive(:perform_later).with(
|
||||
agent_bot.outgoing_url,
|
||||
hash_including(event: 'conversation_status_changed', changed_attributes: anything),
|
||||
hash_including(event: 'conversation_status_changed', account: account.webhook_data, changed_attributes: anything),
|
||||
:agent_bot_webhook,
|
||||
hash_including(secret: agent_bot.secret)
|
||||
).once
|
||||
@@ -158,6 +158,24 @@ describe AgentBotListener do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#conversation_resolved' do
|
||||
let(:event_name) { 'conversation.resolved' }
|
||||
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation) }
|
||||
|
||||
context 'when agent bot is configured' do
|
||||
it 'sends account details in the conversation payload' do
|
||||
create(:agent_bot_inbox, inbox: inbox, agent_bot: agent_bot)
|
||||
expect(AgentBots::WebhookJob).to receive(:perform_later).with(
|
||||
agent_bot.outgoing_url,
|
||||
hash_including(event: 'conversation_resolved', account: account.webhook_data),
|
||||
:agent_bot_webhook,
|
||||
hash_including(secret: agent_bot.secret)
|
||||
).once
|
||||
listener.conversation_resolved(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#webwidget_triggered' do
|
||||
let(:event_name) { 'webwidget.triggered' }
|
||||
|
||||
|
||||
@@ -101,6 +101,17 @@ describe WebhookListener do
|
||||
).once
|
||||
listener.conversation_created(conversation_created_event)
|
||||
end
|
||||
|
||||
it 'includes account details in the conversation payload' do
|
||||
webhook = create(:webhook, inbox: inbox, account: account)
|
||||
expect(WebhookJob).to receive(:perform_later).with(
|
||||
webhook.url,
|
||||
hash_including(account: account.webhook_data),
|
||||
:account_webhook,
|
||||
hash_including(secret: webhook.secret)
|
||||
).once
|
||||
listener.conversation_created(conversation_created_event)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when inbox is an API Channel' do
|
||||
@@ -250,7 +261,7 @@ describe WebhookListener do
|
||||
|
||||
context 'when webhook is configured' do
|
||||
it 'triggers webhook' do
|
||||
inbox_data = Inbox::EventDataPresenter.new(inbox).push_data
|
||||
inbox_data = Inbox::EventDataPresenter.new(inbox).webhook_data
|
||||
webhook = create(:webhook, account: account, subscriptions: ['inbox_created'])
|
||||
expect(WebhookJob).to receive(:perform_later).with(
|
||||
webhook.url, inbox_data.merge(event: 'inbox_created'), :account_webhook,
|
||||
@@ -258,6 +269,17 @@ describe WebhookListener do
|
||||
).once
|
||||
listener.inbox_created(inbox_created_event)
|
||||
end
|
||||
|
||||
it 'includes account details in the inbox payload' do
|
||||
webhook = create(:webhook, account: account, subscriptions: ['inbox_created'])
|
||||
expect(WebhookJob).to receive(:perform_later).with(
|
||||
webhook.url,
|
||||
hash_including(account: account.webhook_data),
|
||||
:account_webhook,
|
||||
hash_including(secret: webhook.secret)
|
||||
).once
|
||||
listener.inbox_created(inbox_created_event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -287,7 +309,7 @@ describe WebhookListener do
|
||||
it 'triggers webhook' do
|
||||
webhook = create(:webhook, account: account, subscriptions: ['inbox_updated'])
|
||||
|
||||
inbox_data = Inbox::EventDataPresenter.new(inbox).push_data
|
||||
inbox_data = Inbox::EventDataPresenter.new(inbox).webhook_data
|
||||
changed_attributes_data = [{ 'name' => { 'previous_value': 'Inbox 1', 'current_value': inbox.name } }]
|
||||
|
||||
expect(WebhookJob).to receive(:perform_later).with(
|
||||
|
||||
@@ -46,6 +46,10 @@ RSpec.describe Conversations::EventDataPresenter do
|
||||
end
|
||||
|
||||
describe '#webhook_data' do
|
||||
it 'includes account details for webhook consumers' do
|
||||
expect(presenter.webhook_data[:account]).to eq(conversation.account.webhook_data)
|
||||
end
|
||||
|
||||
it 'normalizes hard-break backslashes in message content' do
|
||||
message = create(:message, conversation: conversation, account: conversation.account,
|
||||
message_type: :outgoing, content: "Hello\\\nWorld")
|
||||
|
||||
Reference in New Issue
Block a user