From a2c52538be5bf006e4d161a46fdce87627cdfe23 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Tue, 30 Oct 2018 17:00:33 +0100 Subject: [PATCH] Enabled Placetel integration. Added user info via Placetel API lookup (optional). --- .../controllers/_integration/placetel.coffee | 2 + .../app/views/integration/placetel.jst.eco | 16 ++++ .../integration/placetel_controller.rb | 65 +++++++++++++++ app/models/setting.rb | 15 +++- ...> 20181030000001_setting_add_placetel1.rb} | 36 ++++---- db/seeds/settings.rb | 3 +- script/build/cleanup.sh | 1 - spec/requests/integration/placetel_spec.rb | 82 +++++++++++++++---- test/unit/setting_test.rb | 63 ++++++++++++++ 9 files changed, 250 insertions(+), 33 deletions(-) rename db/migrate/{20180921000001_setting_add_placetel.rb => 20181030000001_setting_add_placetel1.rb} (64%) create mode 100644 test/unit/setting_test.rb diff --git a/app/assets/javascripts/app/controllers/_integration/placetel.coffee b/app/assets/javascripts/app/controllers/_integration/placetel.coffee index 8c337700e..21052ef7e 100644 --- a/app/assets/javascripts/app/controllers/_integration/placetel.coffee +++ b/app/assets/javascripts/app/controllers/_integration/placetel.coffee @@ -60,6 +60,8 @@ class Form extends App.Controller config = @config cleanupInput = @cleanupInput + config.api_token = @$('input[name=api_token]').val() + # default caller_id default_caller_id = @$('input[name=default_caller_id]').val() config.outbound.default_caller_id = cleanupInput(default_caller_id) diff --git a/app/assets/javascripts/app/views/integration/placetel.jst.eco b/app/assets/javascripts/app/views/integration/placetel.jst.eco index ae209bed2..530dc02e2 100644 --- a/app/assets/javascripts/app/views/integration/placetel.jst.eco +++ b/app/assets/javascripts/app/views/integration/placetel.jst.eco @@ -19,6 +19,22 @@ +

<%- @T('In order for Zammad to access %s, the %s API token must be stored here', 'Placetel', 'Placetel') %>:

+

+ + + + + + + +
<%- @T('Type') %> + <%- @T('Content') %> +
<%- @T('API Token') %> + +
+
+

<%- @T('Inbound') %>

<%- @T('Blocked caller ids based on sender caller id.') %> diff --git a/app/controllers/integration/placetel_controller.rb b/app/controllers/integration/placetel_controller.rb index 58c0cc215..7e378809e 100644 --- a/app/controllers/integration/placetel_controller.rb +++ b/app/controllers/integration/placetel_controller.rb @@ -20,6 +20,10 @@ class Integration::PlacetelController < ApplicationController local_params['event'] = 'answer' end + if local_params['user'].blank? && local_params['peer'] + local_params['user'] = get_voip_user_by_peer(local_params['peer']) + end + if local_params['direction'].blank? entry = Cti::Log.find_by(call_id: params[:call_id]) if entry @@ -164,4 +168,65 @@ class Integration::PlacetelController < ApplicationController xml_error(error, 401) end + def get_voip_user_by_peer(peer) + load_voip_users[peer] + end + + def load_voip_users + return {} if config_integration.blank? || config_integration[:api_token].blank? + + list = Cache.get('placetelGetVoipUsers') + return list if list + + response = UserAgent.post( + 'https://api.placetel.de/api/getVoIPUsers.json', + { + api_key: config_integration[:api_token], + }, + { + log: { + facility: 'placetel', + }, + json: true, + open_timeout: 4, + read_timeout: 6, + total_timeout: 6, + }, + ) + if !response.success? + logger.error "Can't fetch getVoipUsers from '#{url}', http code: #{response.code}" + Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) + return {} + end + result = response.data + if result.blank? + logger.error "Can't fetch getVoipUsers from '#{url}', result: #{response.inspect}" + Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) + return {} + end + if result.is_a?(Hash) && (result['result'] == '-1' || result['result_code'] == 'error') + logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}" + Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) + return {} + end + if !result.is_a?(Array) + logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}" + Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) + return {} + end + + list = {} + result.each do |entry| + next if entry['name'].blank? + + if entry['uid'].present? + list[entry['uid']] = entry['name'] + end + next if entry['uid2'].blank? + + list[entry['uid2']] = entry['name'] + end + Cache.write('placetelGetVoipUsers', list, { expires_in: 24.hours }) + list + end end diff --git a/app/models/setting.rb b/app/models/setting.rb index ec5853707..6824f8654 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -7,8 +7,8 @@ class Setting < ApplicationModel store :preferences before_create :state_check, :set_initial, :check_broadcast before_update :state_check, :check_broadcast - after_create :reset_change_id - after_update :reset_change_id + after_create :reset_change_id, :reset_cache + after_update :reset_change_id, :reset_cache attr_accessor :state @@ -40,6 +40,7 @@ set config setting setting.state_current = { value: value } setting.save! logger.info "Setting.set('#{name}', #{value.inspect})" + true end =begin @@ -75,6 +76,7 @@ reset config setting to default setting.state_current = setting.state_initial setting.save! logger.info "Setting.reset('#{name}', #{setting.state_current.inspect})" + true end =begin @@ -145,6 +147,15 @@ reload config settings true end + def reset_cache + return true if preferences[:cache].blank? + + preferences[:cache].each do |key| + Cache.delete(key) + end + true + end + # check if cache is still valid def self.cache_valid? if @@lookup_at && @@lookup_at > Time.zone.now - @@lookup_timeout diff --git a/db/migrate/20180921000001_setting_add_placetel.rb b/db/migrate/20181030000001_setting_add_placetel1.rb similarity index 64% rename from db/migrate/20180921000001_setting_add_placetel.rb rename to db/migrate/20181030000001_setting_add_placetel1.rb index 20ffbcc71..5cb6cea3d 100644 --- a/db/migrate/20180921000001_setting_add_placetel.rb +++ b/db/migrate/20181030000001_setting_add_placetel1.rb @@ -1,4 +1,4 @@ -class SettingAddPlacetel < ActiveRecord::Migration[5.1] +class SettingAddPlacetel1 < ActiveRecord::Migration[5.1] def change # return if it's a new setup @@ -32,19 +32,26 @@ class SettingAddPlacetel < ActiveRecord::Migration[5.1] }, frontend: true ) - Setting.create_if_not_exists( - title: 'Placetel config', - name: 'placetel_config', - area: 'Integration::Placetel', - description: 'Defines the Placetel config.', - options: {}, - state: { 'outbound' => { 'routing_table' => [], 'default_caller_id' => '' }, 'inbound' => { 'block_caller_ids' => [] } }, - preferences: { - prio: 2, - permission: ['admin.integration'], - }, - frontend: false, - ) + placetel_config = Setting.find_by(name: 'placetel_config') + if !placetel_config + Setting.create!( + title: 'Placetel config', + name: 'placetel_config', + area: 'Integration::Placetel', + description: 'Defines the Placetel config.', + options: {}, + state: { 'outbound' => { 'routing_table' => [], 'default_caller_id' => '' }, 'inbound' => { 'block_caller_ids' => [] } }, + preferences: { + prio: 2, + permission: ['admin.integration'], + cache: ['placetelGetVoipUsers'], + }, + frontend: false, + ) + else + placetel_config.preferences[:cache] = ['placetelGetVoipUsers'] + placetel_config.save! + end Setting.create_if_not_exists( title: 'PLACETEL Token', name: 'placetel_token', @@ -66,6 +73,5 @@ class SettingAddPlacetel < ActiveRecord::Migration[5.1] }, frontend: false ) - end end diff --git a/db/seeds/settings.rb b/db/seeds/settings.rb index fd26be282..9645459bf 100644 --- a/db/seeds/settings.rb +++ b/db/seeds/settings.rb @@ -4034,6 +4034,7 @@ Setting.create_if_not_exists( preferences: { prio: 2, permission: ['admin.integration'], + cache: ['placetelGetVoipUsers'], }, frontend: false, ) @@ -4041,7 +4042,7 @@ Setting.create_if_not_exists( title: 'PLACETEL Token', name: 'placetel_token', area: 'Integration::Placetel', - description: 'Token for placetel.', + description: 'Token for Placetel.', options: { form: [ { diff --git a/script/build/cleanup.sh b/script/build/cleanup.sh index 20449769d..a4287ae7f 100755 --- a/script/build/cleanup.sh +++ b/script/build/cleanup.sh @@ -5,4 +5,3 @@ set -ex rm app/assets/javascripts/app/controllers/layout_ref.coffee rm -rf app/assets/javascripts/app/views/layout_ref/ rm app/assets/javascripts/app/controllers/karma.coffee -rm app/assets/javascripts/app/controllers/_integration/placetel.coffee diff --git a/spec/requests/integration/placetel_spec.rb b/spec/requests/integration/placetel_spec.rb index 2aa6cbf6d..3b392a668 100644 --- a/spec/requests/integration/placetel_spec.rb +++ b/spec/requests/integration/placetel_spec.rb @@ -187,7 +187,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.from).to eq('4930777000000') expect(log.to).to eq('01114100300') expect(log.direction).to eq('out') - #expect(log.from_comment).to eq('user 1') + expect(log.from_comment).to eq(nil) expect(log.to_comment).to eq('CallerId Customer1') expect(log.comment).to be_nil expect(log.state).to eq('newCall') @@ -207,7 +207,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.from).to eq('4930777000000') expect(log.to).to eq('01114100300') expect(log.direction).to eq('out') - #expect(log.from_comment).to eq('user 1') + expect(log.from_comment).to eq(nil) expect(log.to_comment).to eq('CallerId Customer1') expect(log.comment).to eq('cancel') expect(log.state).to eq('hangup') @@ -227,7 +227,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.from).to eq('4930777000000') expect(log.to).to eq('01114100300') expect(log.direction).to eq('out') - # expect(log.from_comment).to eq('user 1') + expect(log.from_comment).to eq(nil) expect(log.to_comment).to eq('CallerId Customer1') expect(log.comment).to be_nil expect(log.state).to eq('newCall') @@ -247,7 +247,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.from).to eq('4930777000000') expect(log.to).to eq('01114100300') expect(log.direction).to eq('out') - # expect(log.from_comment).to eq('user 1') + expect(log.from_comment).to eq(nil) expect(log.to_comment).to eq('CallerId Customer1') expect(log.comment).to be_nil expect(log.state).to eq('answer') @@ -267,7 +267,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.from).to eq('4930777000000') expect(log.to).to eq('01114100300') expect(log.direction).to eq('out') - # expect(log.from_comment).to eq('user 1') + expect(log.from_comment).to eq(nil) expect(log.to_comment).to eq('CallerId Customer1') expect(log.comment).to eq('normalClearing') expect(log.state).to eq('hangup') @@ -287,7 +287,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('01114100300') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to eq('CallerId Customer1') expect(log.comment).to be_nil expect(log.state).to eq('newCall') @@ -307,7 +307,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('01114100300') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to eq('CallerId Customer1') expect(log.comment).to be_nil expect(log.state).to eq('answer') @@ -327,7 +327,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('01114100300') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to eq('CallerId Customer1') expect(log.comment).to eq('normalClearing') expect(log.state).to eq('hangup') @@ -347,7 +347,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('01114100300') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1,user 2') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to eq('CallerId Customer1') expect(log.comment).to be_nil expect(log.state).to eq('newCall') @@ -407,7 +407,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('01114100300') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1,user 2') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to eq('CallerId Customer1') expect(log.comment).to be_nil expect(log.state).to eq('newCall') @@ -427,7 +427,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('01114100300') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1,user 2') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to eq('CallerId Customer1') expect(log.comment).to eq('normalClearing') expect(log.state).to eq('hangup') @@ -447,7 +447,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('49999992222222') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1,user 2') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to eq('CallerId Customer3,CallerId Customer2') expect(log.preferences['to']).to be_falsey expect(log.preferences['from']).to be_truthy @@ -469,7 +469,7 @@ RSpec.describe 'Integration Placetel', type: :request do expect(log.to).to eq('030600000000') expect(log.from).to eq('anonymous') expect(log.direction).to eq('in') - #expect(log.to_comment).to eq('user 1,user 2') + expect(log.to_comment).to eq(nil) expect(log.from_comment).to be_nil expect(log.preferences['to']).to be_falsey expect(log.preferences['from']).to be_falsey @@ -503,12 +503,66 @@ RSpec.describe 'Integration Placetel', type: :request do expect(json_response['list'][5]['call_id']).to eq('1234567890-2') expect(json_response['list'][5]['state']).to eq('hangup') expect(json_response['list'][5]['from']).to eq('4930777000000') - #expect(json_response['list'][5]['from_comment']).to eq('user 1') + expect(json_response['list'][5]['from_comment']).to eq(nil) expect(json_response['list'][5]['to']).to eq('01114100300') expect(json_response['list'][5]['to_comment']).to eq('CallerId Customer1') expect(json_response['list'][5]['comment']).to eq('normalClearing') expect(json_response['list'][5]['state']).to eq('hangup') expect(json_response['list'][6]['call_id']).to eq('1234567890-1') end + + it 'does log call with peer' do + token = Setting.get('placetel_token') + + # outbound - I - new call + params = 'event=newCall&direction=out&from=030600000000&to=01114100300&call_id=1234567890-1&peer=something@example.com' + post "/api/v1/placetel/#{token}", params: params + expect(response).to have_http_status(200) + log = Cti::Log.find_by(call_id: '1234567890-1') + expect(log).to be_truthy + expect(log.from).to eq('4930777000000') + expect(log.to).to eq('01114100300') + expect(log.direction).to eq('out') + expect(log.from_comment).to eq(nil) + expect(log.to_comment).to eq('CallerId Customer1') + expect(log.comment).to be_nil + expect(log.state).to eq('newCall') + expect(log.done).to eq(true) + expect(log.initialized_at).to be_truthy + expect(log.start_at).to be_nil + expect(log.end_at).to be_nil + expect(log.duration_waiting_time).to be_nil + expect(log.duration_talking_time).to be_nil + + config = Setting.get('placetel_config') + config[:api_token] = '123' + Setting.set('placetel_config', config) + + stub_request(:post, 'https://api.placetel.de/api/getVoIPUsers.json') + .to_return(status: 200, body: [{ 'callerid' => '03055571600', 'did' => 10, 'name' => 'Bob Smith', 'stype' => 3, 'uid' => '777008478072@example.com', 'uid2' => nil }, { 'callerid' => '03055571600', 'did' => 12, 'name' => 'Josef Müller', 'stype' => 3, 'uid' => '777042617425@example.com', 'uid2' => nil }].to_json) + + params = 'event=newCall&direction=out&from=030600000000&to=01114100300&call_id=1234567890-2&peer=777008478072@example.com' + post "/api/v1/placetel/#{token}", params: params + expect(response).to have_http_status(200) + log = Cti::Log.find_by(call_id: '1234567890-2') + expect(log).to be_truthy + expect(log.from).to eq('4930777000000') + expect(log.to).to eq('01114100300') + expect(log.direction).to eq('out') + expect(log.from_comment).to eq('Bob Smith') + expect(log.to_comment).to eq('CallerId Customer1') + expect(log.comment).to be_nil + expect(log.state).to eq('newCall') + expect(log.done).to eq(true) + expect(log.initialized_at).to be_truthy + expect(log.start_at).to be_nil + expect(log.end_at).to be_nil + expect(log.duration_waiting_time).to be_nil + expect(log.duration_talking_time).to be_nil + + # check if cache is filled + expect(Cache.get('placetelGetVoipUsers')['777008478072@example.com']).to eq('Bob Smith') + + end end end diff --git a/test/unit/setting_test.rb b/test/unit/setting_test.rb new file mode 100644 index 000000000..32ad22c6d --- /dev/null +++ b/test/unit/setting_test.rb @@ -0,0 +1,63 @@ +require 'test_helper' + +class SettingTest < ActiveSupport::TestCase + + test 'basics' do + Setting.create!( + title: 'ABC API Token', + name: 'abc_api_token', + area: 'Integration::ABC', + description: 'API Token for ABC to access ABC.', + options: { + form: [ + { + display: '', + null: false, + name: 'abc_token', + tag: 'input', + }, + ], + }, + state: 'abc', + frontend: false + ) + assert_equal(Setting.get('abc_api_token'), 'abc') + assert(Setting.set('abc_api_token', 'new_abc')) + assert_equal(Setting.get('abc_api_token'), 'new_abc') + assert(Setting.reset('abc_api_token')) + assert_equal(Setting.get('abc_api_token'), 'abc') + end + + test 'cache reset via preferences' do + Setting.create!( + title: 'ABC API Token', + name: 'abc_api_token', + area: 'Integration::ABC', + description: 'API Token for ABC to access ABC.', + options: { + form: [ + { + display: '', + null: false, + name: 'abc_token', + tag: 'input', + }, + ], + }, + state: '', + preferences: { + permission: ['admin.integration'], + cache: ['abcGetVoipUsers'], + }, + frontend: false + ) + + Cache.write('abcGetVoipUsers', { a: 1 }) + assert_equal(Cache.get('abcGetVoipUsers'), { a: 1 }) + + Setting.set('abc_api_token', 'some_new_value') + assert_nil(Cache.get('abcGetVoipUsers')) + + end + +end