From 653e0335c07000f58f8b09799c09bc1f426dd7b9 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Mon, 30 Oct 2023 13:24:03 -0700 Subject: [PATCH] fix: Handle PermissionDeniedError for Dialogflow processor (#8252) --- .../channel_notifications_mailer.rb | 7 +++ app/models/concerns/reauthorizable.rb | 18 ++++++-- app/models/inbox.rb | 2 +- app/models/integrations/hook.rb | 4 ++ .../dialogflow_disconnect.liquid | 3 ++ .../dialogflow/processor_service.rb | 43 +++++++++++++------ .../dialogflow/processor_service_spec.rb | 9 +++- .../channel_notifications_mailer_spec.rb | 16 +++++++ 8 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 app/views/mailers/administrator_notifications/channel_notifications_mailer/dialogflow_disconnect.liquid diff --git a/app/mailers/administrator_notifications/channel_notifications_mailer.rb b/app/mailers/administrator_notifications/channel_notifications_mailer.rb index 00a3830b1..b25a6d66b 100644 --- a/app/mailers/administrator_notifications/channel_notifications_mailer.rb +++ b/app/mailers/administrator_notifications/channel_notifications_mailer.rb @@ -7,6 +7,13 @@ class AdministratorNotifications::ChannelNotificationsMailer < ApplicationMailer send_mail_with_liquid(to: admin_emails, subject: subject) and return end + def dialogflow_disconnect + return unless smtp_config_set_or_development? + + subject = 'Your Dialogflow integration was disconnected' + send_mail_with_liquid(to: admin_emails, subject: subject) and return + end + def facebook_disconnect(inbox) return unless smtp_config_set_or_development? diff --git a/app/models/concerns/reauthorizable.rb b/app/models/concerns/reauthorizable.rb index 6186dcc15..fa46d0060 100644 --- a/app/models/concerns/reauthorizable.rb +++ b/app/models/concerns/reauthorizable.rb @@ -39,15 +39,25 @@ module Reauthorizable def prompt_reauthorization! ::Redis::Alfred.set(reauthorization_required_key, true) + mailer = AdministratorNotifications::ChannelNotificationsMailer.with(account: account) + case self.class.name when 'Integrations::Hook' - AdministratorNotifications::ChannelNotificationsMailer.with(account: account).slack_disconnect.deliver_later if slack? + process_integration_hook_reauthorization_emails(mailer) when 'Channel::FacebookPage' - AdministratorNotifications::ChannelNotificationsMailer.with(account: account).facebook_disconnect(inbox).deliver_later + mailer.facebook_disconnect(inbox).deliver_later when 'Channel::Whatsapp' - AdministratorNotifications::ChannelNotificationsMailer.with(account: account).whatsapp_disconnect(inbox).deliver_later + mailer.whatsapp_disconnect(inbox).deliver_later when 'Channel::Email' - AdministratorNotifications::ChannelNotificationsMailer.with(account: account).email_disconnect(inbox).deliver_later + mailer.email_disconnect(inbox).deliver_later + end + end + + def process_integration_hook_reauthorization_emails(mailer) + if slack? + mailer.slack_disconnect.deliver_later + elsif dialogflow? + mailer.dialogflow_disconnect.deliver_later end end diff --git a/app/models/inbox.rb b/app/models/inbox.rb index 5a13b114f..311d7369b 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -129,7 +129,7 @@ class Inbox < ApplicationRecord end def active_bot? - agent_bot_inbox&.active? || hooks.pluck(:app_id).include?('dialogflow') + agent_bot_inbox&.active? || hooks.where(app_id: 'dialogflow', status: 'enabled').count.positive? end def inbox_type diff --git a/app/models/integrations/hook.rb b/app/models/integrations/hook.rb index 9d80b245b..076a5fce9 100644 --- a/app/models/integrations/hook.rb +++ b/app/models/integrations/hook.rb @@ -43,6 +43,10 @@ class Integrations::Hook < ApplicationRecord app_id == 'slack' end + def dialogflow? + app_id == 'dialogflow' + end + def disable update(status: 'disabled') end diff --git a/app/views/mailers/administrator_notifications/channel_notifications_mailer/dialogflow_disconnect.liquid b/app/views/mailers/administrator_notifications/channel_notifications_mailer/dialogflow_disconnect.liquid new file mode 100644 index 000000000..b60db35c1 --- /dev/null +++ b/app/views/mailers/administrator_notifications/channel_notifications_mailer/dialogflow_disconnect.liquid @@ -0,0 +1,3 @@ +

Hello there,

+ +

Your Dialogflow integration was disconnected because of permission issues. To resolve this, please delete the integration from the admin dashboard and reconnect it using new credentials.

diff --git a/lib/integrations/dialogflow/processor_service.rb b/lib/integrations/dialogflow/processor_service.rb index 3e7f1cdf6..103ef05c5 100644 --- a/lib/integrations/dialogflow/processor_service.rb +++ b/lib/integrations/dialogflow/processor_service.rb @@ -19,15 +19,12 @@ class Integrations::Dialogflow::ProcessorService < Integrations::BotProcessorSer Rails.logger.warn "Account: #{hook.try(:account_id)} Hook: #{hook.id} credentials are not present." && return end - ::Google::Cloud::Dialogflow::V2::Sessions::Client.configure do |config| - config.timeout = 10.0 - config.credentials = hook.settings['credentials'] - end - - client = ::Google::Cloud::Dialogflow::V2::Sessions::Client.new - session = "projects/#{hook.settings['project_id']}/agent/sessions/#{session_id}" - query_input = { text: { text: message, language_code: 'en-US' } } - client.detect_intent session: session, query_input: query_input + configure_dialogflow_client_defaults + detect_intent(session_id, message) + rescue Google::Cloud::PermissionDeniedError => e + Rails.logger.warn "DialogFlow Error: (account-#{hook.try(:account_id)}, hook-#{hook.id}) #{e.message}" + hook.prompt_reauthorization! + hook.disable end def process_response(message, response) @@ -53,10 +50,28 @@ class Integrations::Dialogflow::ProcessorService < Integrations::BotProcessorSer return if content_params.blank? conversation = message.conversation - conversation.messages.create!(content_params.merge({ - message_type: :outgoing, - account_id: conversation.account_id, - inbox_id: conversation.inbox_id - })) + conversation.messages.create!( + content_params.merge( + { + message_type: :outgoing, + account_id: conversation.account_id, + inbox_id: conversation.inbox_id + } + ) + ) + end + + def configure_dialogflow_client_defaults + ::Google::Cloud::Dialogflow::V2::Sessions::Client.configure do |config| + config.timeout = 10.0 + config.credentials = hook.settings['credentials'] + end + end + + def detect_intent(session_id, message) + client = ::Google::Cloud::Dialogflow::V2::Sessions::Client.new + session = "projects/#{hook.settings['project_id']}/agent/sessions/#{session_id}" + query_input = { text: { text: message, language_code: 'en-US' } } + client.detect_intent session: session, query_input: query_input end end diff --git a/spec/lib/integrations/dialogflow/processor_service_spec.rb b/spec/lib/integrations/dialogflow/processor_service_spec.rb index 9f7535f5f..2b5f36c5a 100644 --- a/spec/lib/integrations/dialogflow/processor_service_spec.rb +++ b/spec/lib/integrations/dialogflow/processor_service_spec.rb @@ -162,10 +162,17 @@ describe Integrations::Dialogflow::ProcessorService do allow(session_client).to receive(:detect_intent).and_return({ session: session, query_input: query_input }) end - it 'returns indented response' do + it 'returns intended response' do response = processor.send(:get_response, conversation.contact_inbox.source_id, message.content) expect(response[:query_input][:text][:text]).to eq(message) expect(response[:query_input][:text][:language_code]).to eq('en-US') end + + it 'disables the hook if permission errors are thrown' do + allow(session_client).to receive(:detect_intent).and_raise(Google::Cloud::PermissionDeniedError) + + expect { processor.send(:get_response, conversation.contact_inbox.source_id, message.content) } + .to change(hook, :status).from('enabled').to('disabled') + end end end diff --git a/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb b/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb index 676d2afaf..f67b8300c 100644 --- a/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb +++ b/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb @@ -25,6 +25,22 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer do end end + describe 'dialogflow disconnect' do + let(:mail) { described_class.with(account: account).dialogflow_disconnect.deliver_now } + + it 'renders the subject' do + expect(mail.subject).to eq('Your Dialogflow integration was disconnected') + end + + it 'renders the content' do + expect(mail.body).to include('Your Dialogflow integration was disconnected because of permission issues.') + end + + it 'renders the receiver email' do + expect(mail.to).to eq([administrator.email]) + end + end + describe 'facebook_disconnect' do before do stub_request(:post, /graph.facebook.com/)