chore: Add reauthorization prompt for Whatsapp Channel (#5929)
- Add reauthorization prompt for Whatsapp Channel fixes: #5782
This commit is contained in:
parent
606fc9046a
commit
8ea0660862
11 changed files with 148 additions and 7 deletions
|
@ -4,6 +4,7 @@ class Webhooks::WhatsappEventsJob < ApplicationJob
|
||||||
def perform(params = {})
|
def perform(params = {})
|
||||||
channel = find_channel_from_whatsapp_business_payload(params) || find_channel(params)
|
channel = find_channel_from_whatsapp_business_payload(params) || find_channel(params)
|
||||||
return if channel.blank?
|
return if channel.blank?
|
||||||
|
return if channel.reauthorization_required?
|
||||||
|
|
||||||
case channel.provider
|
case channel.provider
|
||||||
when 'whatsapp_cloud'
|
when 'whatsapp_cloud'
|
||||||
|
|
|
@ -15,6 +15,14 @@ class AdministratorNotifications::ChannelNotificationsMailer < ApplicationMailer
|
||||||
send_mail_with_liquid(to: admin_emails, subject: subject) and return
|
send_mail_with_liquid(to: admin_emails, subject: subject) and return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def whatsapp_disconnect(inbox)
|
||||||
|
return unless smtp_config_set_or_development?
|
||||||
|
|
||||||
|
subject = 'Your Whatsapp connection has expired'
|
||||||
|
@action_url = "#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{Current.account.id}/settings/inboxes/#{inbox.id}"
|
||||||
|
send_mail_with_liquid(to: admin_emails, subject: subject) and return
|
||||||
|
end
|
||||||
|
|
||||||
def email_disconnect(inbox)
|
def email_disconnect(inbox)
|
||||||
return unless smtp_config_set_or_development?
|
return unless smtp_config_set_or_development?
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
class Channel::Whatsapp < ApplicationRecord
|
class Channel::Whatsapp < ApplicationRecord
|
||||||
include Channelable
|
include Channelable
|
||||||
|
include Reauthorizable
|
||||||
|
|
||||||
self.table_name = 'channel_whatsapp'
|
self.table_name = 'channel_whatsapp'
|
||||||
EDITABLE_ATTRS = [:phone_number, :provider, { provider_config: {} }].freeze
|
EDITABLE_ATTRS = [:phone_number, :provider, { provider_config: {} }].freeze
|
||||||
|
|
|
@ -39,11 +39,14 @@ module Reauthorizable
|
||||||
def prompt_reauthorization!
|
def prompt_reauthorization!
|
||||||
::Redis::Alfred.set(reauthorization_required_key, true)
|
::Redis::Alfred.set(reauthorization_required_key, true)
|
||||||
|
|
||||||
if (is_a? Integrations::Hook) && slack?
|
case self.class.name
|
||||||
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).slack_disconnect.deliver_later
|
when 'Integrations::Hook'
|
||||||
elsif is_a? Channel::FacebookPage
|
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).slack_disconnect.deliver_later if slack?
|
||||||
|
when 'Channel::FacebookPage'
|
||||||
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).facebook_disconnect(inbox).deliver_later
|
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).facebook_disconnect(inbox).deliver_later
|
||||||
elsif is_a? Channel::Email
|
when 'Channel::Whatsapp'
|
||||||
|
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).whatsapp_disconnect(inbox).deliver_later
|
||||||
|
when 'Channel::Email'
|
||||||
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).email_disconnect(inbox).deliver_later
|
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).email_disconnect(inbox).deliver_later
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,9 +96,11 @@ class Whatsapp::IncomingMessageBaseService
|
||||||
return if %w[text button interactive location].include?(message_type)
|
return if %w[text button interactive location].include?(message_type)
|
||||||
|
|
||||||
attachment_payload = @processed_params[:messages].first[message_type.to_sym]
|
attachment_payload = @processed_params[:messages].first[message_type.to_sym]
|
||||||
attachment_file = download_attachment_file(attachment_payload)
|
|
||||||
|
|
||||||
@message.content ||= attachment_payload[:caption]
|
@message.content ||= attachment_payload[:caption]
|
||||||
|
|
||||||
|
attachment_file = download_attachment_file(attachment_payload)
|
||||||
|
return if attachment_file.blank?
|
||||||
|
|
||||||
@message.attachments.new(
|
@message.attachments.new(
|
||||||
account_id: @message.account_id,
|
account_id: @message.account_id,
|
||||||
file_type: file_content_type(message_type),
|
file_type: file_content_type(message_type),
|
||||||
|
|
|
@ -10,6 +10,8 @@ class Whatsapp::IncomingMessageWhatsappCloudService < Whatsapp::IncomingMessageB
|
||||||
|
|
||||||
def download_attachment_file(attachment_payload)
|
def download_attachment_file(attachment_payload)
|
||||||
url_response = HTTParty.get(inbox.channel.media_url(attachment_payload[:id]), headers: inbox.channel.api_headers)
|
url_response = HTTParty.get(inbox.channel.media_url(attachment_payload[:id]), headers: inbox.channel.api_headers)
|
||||||
Down.download(url_response.parsed_response['url'], headers: inbox.channel.api_headers)
|
# This url response will be failure if the access token has expired.
|
||||||
|
inbox.channel.authorization_error! if url_response.unauthorized?
|
||||||
|
Down.download(url_response.parsed_response['url'], headers: inbox.channel.api_headers) if url_response.success?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<p>Hello,</p>
|
||||||
|
|
||||||
|
<p>Your Whatsapp Access has expired. </p>
|
||||||
|
<p>Please reconnect Whatsapp to continue receiving messages.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Click <a href="{{action_url}}">here</a> to re-connect.
|
||||||
|
</p>
|
|
@ -23,6 +23,13 @@ RSpec.describe Webhooks::WhatsappEventsJob, type: :job do
|
||||||
expect(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new)
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new)
|
||||||
job.perform_now(params)
|
job.perform_now(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'will not enques Whatsapp::IncomingMessageWhatsappCloudService if channel reauthorization required' do
|
||||||
|
channel.prompt_reauthorization!
|
||||||
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
||||||
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).not_to receive(:new)
|
||||||
|
job.perform_now(params)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when default provider' do
|
context 'when default provider' do
|
||||||
|
|
|
@ -41,4 +41,18 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer, type: :ma
|
||||||
expect(mail.to).to eq([administrator.email])
|
expect(mail.to).to eq([administrator.email])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'whatsapp_disconnect' do
|
||||||
|
let!(:whatsapp_channel) { create(:channel_whatsapp, provider: 'whatsapp_cloud', sync_templates: false, validate_provider_config: false) }
|
||||||
|
let!(:whatsapp_inbox) { create(:inbox, channel: whatsapp_channel, account: account) }
|
||||||
|
let(:mail) { described_class.with(account: account).whatsapp_disconnect(whatsapp_inbox).deliver_now }
|
||||||
|
|
||||||
|
it 'renders the subject' do
|
||||||
|
expect(mail.subject).to eq('Your Whatsapp connection has expired')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders the receiver email' do
|
||||||
|
expect(mail.to).to eq([administrator.email])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,33 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
require Rails.root.join 'spec/models/concerns/reauthorizable_shared.rb'
|
||||||
|
|
||||||
RSpec.describe Channel::Whatsapp do
|
RSpec.describe Channel::Whatsapp do
|
||||||
|
describe 'concerns' do
|
||||||
|
let(:channel) { create(:channel_whatsapp) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
|
||||||
|
stub_request(:get, 'https://waba.360dialog.io/v1/configs/templates')
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'reauthorizable'
|
||||||
|
|
||||||
|
context 'when prompt_reauthorization!' do
|
||||||
|
it 'calls channel notifier mail for whatsapp' do
|
||||||
|
admin_mailer = double
|
||||||
|
mailer_double = double
|
||||||
|
|
||||||
|
expect(AdministratorNotifications::ChannelNotificationsMailer).to receive(:with).and_return(admin_mailer)
|
||||||
|
expect(admin_mailer).to receive(:whatsapp_disconnect).with(channel.inbox).and_return(mailer_double)
|
||||||
|
expect(mailer_double).to receive(:deliver_later)
|
||||||
|
|
||||||
|
channel.prompt_reauthorization!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'validate_provider_config' do
|
describe 'validate_provider_config' do
|
||||||
let(:channel) { build(:channel_whatsapp, provider: 'whatsapp_cloud', account: create(:account)) }
|
let(:channel) { build(:channel_whatsapp, provider: 'whatsapp_cloud', account: create(:account)) }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Whatsapp::IncomingMessageWhatsappCloudService do
|
||||||
|
describe '#perform' do
|
||||||
|
let!(:whatsapp_channel) { create(:channel_whatsapp, provider: 'whatsapp_cloud', sync_templates: false, validate_provider_config: false) }
|
||||||
|
let(:params) do
|
||||||
|
{
|
||||||
|
phone_number: whatsapp_channel.phone_number,
|
||||||
|
object: 'whatsapp_business_account',
|
||||||
|
entry: [{
|
||||||
|
changes: [{
|
||||||
|
value: {
|
||||||
|
contacts: [{ profile: { name: 'Sojan Jose' }, wa_id: '2423423243' }],
|
||||||
|
messages: [{
|
||||||
|
from: '2423423243',
|
||||||
|
image: {
|
||||||
|
id: 'b1c68f38-8734-4ad3-b4a1-ef0c10d683',
|
||||||
|
mime_type: 'image/jpeg',
|
||||||
|
sha256: '29ed500fa64eb55fc19dc4124acb300e5dcca0f822a301ae99944db',
|
||||||
|
caption: 'Check out my product!'
|
||||||
|
},
|
||||||
|
timestamp: '1664799904', type: 'image'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when valid attachment message params' do
|
||||||
|
it 'creates appropriate conversations, message and contacts' do
|
||||||
|
stub_request(:get, whatsapp_channel.media_url('b1c68f38-8734-4ad3-b4a1-ef0c10d683')).to_return(
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
messaging_product: 'whatsapp',
|
||||||
|
url: 'https://chatwoot-assets.local/sample.png',
|
||||||
|
mime_type: 'image/jpeg',
|
||||||
|
sha256: 'sha256',
|
||||||
|
file_size: 'SIZE',
|
||||||
|
id: 'b1c68f38-8734-4ad3-b4a1-ef0c10d683'
|
||||||
|
}.to_json,
|
||||||
|
headers: { 'content-type' => 'application/json' }
|
||||||
|
)
|
||||||
|
stub_request(:get, 'https://chatwoot-assets.local/sample.png').to_return(
|
||||||
|
status: 200,
|
||||||
|
body: File.read('spec/assets/sample.png')
|
||||||
|
)
|
||||||
|
|
||||||
|
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
|
||||||
|
expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
|
||||||
|
expect(Contact.all.first.name).to eq('Sojan Jose')
|
||||||
|
expect(whatsapp_channel.inbox.messages.first.content).to eq('Check out my product!')
|
||||||
|
expect(whatsapp_channel.inbox.messages.first.attachments.present?).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'increments reauthorization count if fetching attachment fails' do
|
||||||
|
stub_request(:get, whatsapp_channel.media_url('b1c68f38-8734-4ad3-b4a1-ef0c10d683')).to_return(
|
||||||
|
status: 401
|
||||||
|
)
|
||||||
|
|
||||||
|
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
|
||||||
|
expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
|
||||||
|
expect(Contact.all.first.name).to eq('Sojan Jose')
|
||||||
|
expect(whatsapp_channel.inbox.messages.first.content).to eq('Check out my product!')
|
||||||
|
expect(whatsapp_channel.inbox.messages.first.attachments.present?).to be false
|
||||||
|
expect(whatsapp_channel.authorization_error_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue