chore: Add region option to Dialogflow integration (#11510)

## Summary
- support region option when configuring Dialogflow integration
- connect to region endpoint when set
- use session identification based on the region

Fixes: https://github.com/chatwoot/chatwoot/issues/4129
This commit is contained in:
Sojan Jose
2025-06-05 20:01:17 -05:00
committed by GitHub
parent 27bce50210
commit 10363e77ad
5 changed files with 103 additions and 4 deletions

View File

@@ -84,6 +84,7 @@ dialogflow:
{
'project_id': { 'type': 'string' },
'credentials': { 'type': 'object' },
'region': { 'type': 'string' },
},
'required': ['project_id', 'credentials'],
'additionalProperties': false,
@@ -106,8 +107,21 @@ dialogflow:
'validation-messages':
{ 'JSON': 'Invalid JSON', 'required': 'Credentials is required' },
},
{
'label': 'Dialogflow Region',
'type': 'select',
'name': 'region',
'default': 'global',
'options': [
{ 'label': 'Global - Default', 'value': 'global' },
{ 'label': 'AS-NE1 - Tokyo, Japan', 'value': 'asia-northeast1' },
{ 'label': 'AU-SE1 - Sydney, Australia', 'value': 'australia-southeast1' },
{ 'label': 'EU-W1 - St. Ghislain, Belgium', 'value': 'europe-west1' },
{ 'label': 'EU-W2 - London, England', 'value': 'europe-west2' },
],
},
]
visible_properties: ['project_id']
visible_properties: ['project_id', 'region']
google_translate:
id: google_translate
logo: google-translate.png

View File

@@ -65,13 +65,37 @@ class Integrations::Dialogflow::ProcessorService < Integrations::BotProcessorSer
::Google::Cloud::Dialogflow::V2::Sessions::Client.configure do |config|
config.timeout = 10.0
config.credentials = hook.settings['credentials']
config.endpoint = dialogflow_endpoint
end
end
def normalized_region
region = hook.settings['region'].to_s.strip
(region.presence || 'global')
end
def dialogflow_endpoint
region = normalized_region
return 'dialogflow.googleapis.com' if region == 'global'
"#{region}-dialogflow.googleapis.com"
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}"
session = build_session_path(session_id)
query_input = { text: { text: message, language_code: 'en-US' } }
client.detect_intent session: session, query_input: query_input
end
def build_session_path(session_id)
project_id = hook.settings['project_id']
region = normalized_region
if region == 'global'
"projects/#{project_id}/agent/sessions/#{session_id}"
else
"projects/#{project_id}/locations/#{region}/agent/sessions/#{session_id}"
end
end
end

View File

@@ -5,7 +5,7 @@ RSpec.describe 'Integration Hooks API', type: :request do
let(:admin) { create(:user, account: account, role: :administrator) }
let(:agent) { create(:user, account: account, role: :agent) }
let(:inbox) { create(:inbox, account: account) }
let(:params) { { app_id: 'dialogflow', inbox_id: inbox.id, settings: { project_id: 'xx', credentials: { test: 'test' } } } }
let(:params) { { app_id: 'dialogflow', inbox_id: inbox.id, settings: { project_id: 'xx', credentials: { test: 'test' }, region: 'europe-west1' } } }
describe 'POST /api/v1/accounts/{account.id}/integrations/hooks' do
context 'when it is an unauthenticated user' do

View File

@@ -9,7 +9,7 @@ FactoryBot.define do
trait :dialogflow do
app_id { 'dialogflow' }
settings { { project_id: 'test', credentials: {} } }
settings { { project_id: 'test', credentials: {}, region: 'global' } }
end
trait :dyte do

View File

@@ -175,4 +175,65 @@ describe Integrations::Dialogflow::ProcessorService do
.to change(hook, :status).from('enabled').to('disabled')
end
end
describe 'region configuration' do
let(:processor) { described_class.new(event_name: event_name, hook: hook, event_data: event_data) }
context 'when region is global or not specified' do
it 'uses global endpoint and session path' do
hook.update(settings: { 'project_id' => 'test-project', 'credentials' => {} })
expect(processor.send(:dialogflow_endpoint)).to eq('dialogflow.googleapis.com')
expect(processor.send(:build_session_path, 'test-session')).to eq('projects/test-project/agent/sessions/test-session')
end
end
context 'when region is specified' do
it 'uses regional endpoint and session path' do
hook.update(settings: { 'project_id' => 'test-project', 'credentials' => {}, 'region' => 'europe-west1' })
expect(processor.send(:dialogflow_endpoint)).to eq('europe-west1-dialogflow.googleapis.com')
expect(processor.send(:build_session_path, 'test-session')).to eq('projects/test-project/locations/europe-west1/agent/sessions/test-session')
end
end
it 'configures client with correct endpoint' do
hook.update(settings: { 'project_id' => 'test', 'credentials' => {}, 'region' => 'europe-west1' })
config = OpenStruct.new
expect(Google::Cloud::Dialogflow::V2::Sessions::Client).to receive(:configure).and_yield(config)
processor.send(:configure_dialogflow_client_defaults)
expect(config.endpoint).to eq('europe-west1-dialogflow.googleapis.com')
end
context 'when calling detect_intent' do
let(:mock_client) { instance_double(Google::Cloud::Dialogflow::V2::Sessions::Client) }
before do
allow(Google::Cloud::Dialogflow::V2::Sessions::Client).to receive(:new).and_return(mock_client)
end
it 'uses global session path when region is not specified' do
hook.update(settings: { 'project_id' => 'test-project', 'credentials' => {} })
expect(mock_client).to receive(:detect_intent).with(
session: 'projects/test-project/agent/sessions/test-session',
query_input: { text: { text: 'Hello', language_code: 'en-US' } }
)
processor.send(:detect_intent, 'test-session', 'Hello')
end
it 'uses regional session path when region is specified' do
hook.update(settings: { 'project_id' => 'test-project', 'credentials' => {}, 'region' => 'europe-west1' })
expect(mock_client).to receive(:detect_intent).with(
session: 'projects/test-project/locations/europe-west1/agent/sessions/test-session',
query_input: { text: { text: 'Hello', language_code: 'en-US' } }
)
processor.send(:detect_intent, 'test-session', 'Hello')
end
end
end
end