diff --git a/config/integration/apps.yml b/config/integration/apps.yml index 2921bf637..1faf35670 100644 --- a/config/integration/apps.yml +++ b/config/integration/apps.yml @@ -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 diff --git a/lib/integrations/dialogflow/processor_service.rb b/lib/integrations/dialogflow/processor_service.rb index f3962a66c..578a277a2 100644 --- a/lib/integrations/dialogflow/processor_service.rb +++ b/lib/integrations/dialogflow/processor_service.rb @@ -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 diff --git a/spec/controllers/api/v1/accounts/integrations/hooks_controller_spec.rb b/spec/controllers/api/v1/accounts/integrations/hooks_controller_spec.rb index 23c1045c5..5ca2633fc 100644 --- a/spec/controllers/api/v1/accounts/integrations/hooks_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/integrations/hooks_controller_spec.rb @@ -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 diff --git a/spec/factories/integrations/hooks.rb b/spec/factories/integrations/hooks.rb index c2d227e78..85517335a 100644 --- a/spec/factories/integrations/hooks.rb +++ b/spec/factories/integrations/hooks.rb @@ -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 diff --git a/spec/lib/integrations/dialogflow/processor_service_spec.rb b/spec/lib/integrations/dialogflow/processor_service_spec.rb index 2b5f36c5a..3160f74d3 100644 --- a/spec/lib/integrations/dialogflow/processor_service_spec.rb +++ b/spec/lib/integrations/dialogflow/processor_service_spec.rb @@ -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