diff --git a/app/assets/javascripts/app/controllers/cti.coffee b/app/assets/javascripts/app/controllers/cti.coffee
new file mode 100644
index 000000000..08b09ec27
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/cti.coffee
@@ -0,0 +1,114 @@
+class App.CTI extends App.Controller
+ constructor: ->
+ super
+
+ @meta =
+ active: false
+
+ preferences = @Session.get('preferences') || {}
+ @meta.active = preferences.cti || false
+
+ @load()
+
+ App.Event.bind(
+ 'cti_event'
+ (data) =>
+ console.log('cti_event', data)
+ if data.state is 'newCall'
+ console.log('notify')
+ @notify(data)
+ 'cti_event'
+ )
+ App.Event.bind(
+ 'cti_list_push'
+ (data) =>
+ @list = data
+ @render()
+ 'cti_list_push'
+ )
+
+ # fetch data, render view
+ load: ->
+ @ajax(
+ id: 'cti_log'
+ type: 'GET'
+ url: "#{@apiPath}/cti/log"
+ success: (data) =>
+ @list = data
+ @render()
+ )
+
+ notify: (data) ->
+ console.log(data)
+ #return if !
+ if data.state is 'newCall' && data.direction is 'in'
+ App.Event.trigger 'notify', {
+ type: 'notice'
+ msg: App.i18n.translateContent('Call from %s for %s', data.from, data.to)
+ timeout: 2500
+ }
+
+ featureActive: =>
+ return true
+ if @Config.get('sipgate_integration')
+ return true
+ false
+
+ render: ->
+ if !@isRole('CTI')
+ @renderScreenUnauthorized(objectName: 'CTI')
+ return
+
+ @html App.view('cti/index')(
+ list: @list
+ )
+
+ @updateNavMenu()
+
+ show: (params) =>
+ @title 'CTI', true
+ @navupdate '#cti'
+
+ counter: ->
+ counter = 0
+
+ switch: (state = undefined) =>
+
+ # read state
+ if state is undefined
+ return @meta.active
+
+ @meta.active = state
+
+ # update user preferences
+ @ajax(
+ id: 'preferences'
+ type: 'PUT'
+ url: "#{@apiPath}/users/preferences"
+ data: JSON.stringify(user: {cti: state})
+ processData: true
+ )
+
+ updateNavMenu: =>
+ delay = ->
+ App.Event.trigger('menu:render')
+ @delay(delay, 200, 'updateNavMenu')
+
+class CTIRouter extends App.ControllerPermanent
+ constructor: (params) ->
+ super
+
+ # check authentication
+ return if !@authenticate(false, 'CTI')
+
+ App.TaskManager.execute(
+ key: 'CTI'
+ controller: 'CTI'
+ params: {}
+ show: true
+ persistent: true
+ )
+
+App.Config.set('cti', CTIRouter, 'Routes')
+App.Config.set('CTI', { controller: 'CTI', authentication: true }, 'permanentTask')
+App.Config.set('CTI', { prio: 1300, parent: '', name: 'Phone', target: '#cti', key: 'CTI', shown: false, role: ['CTI'], class: 'phone' }, 'NavBar')
diff --git a/app/assets/javascripts/app/views/cti/index.jst.eco b/app/assets/javascripts/app/views/cti/index.jst.eco
new file mode 100644
index 000000000..d1f660668
--- /dev/null
+++ b/app/assets/javascripts/app/views/cti/index.jst.eco
@@ -0,0 +1,28 @@
+
+
<%- @T('Caller log') %>
+
+
+
+
+
+ <%- @T('From') %> |
+ <%- @T('To') %> |
+ <%- @T('State') %> |
+ <%- @T('Comment') %> |
+ <%- @T('Time') %> |
+
+
+
+ <% for item in @list: %>
+
+ <%= item.from %> |
+ <%= item.to %> |
+ <%= item.state %> |
+ <%= item.comment %> |
+ <%- @humanTime(item.created_at) %> |
+
+ <% end %>
+
+
+
+
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 27dfe7ccd..37a12b9d3 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -287,12 +287,11 @@ class ApplicationController < ActionController::Base
}
end
- def authentication_check(auth_param = {} )
+ def authentication_check(auth_param = {})
result = authentication_check_only(auth_param)
# check if basic_auth fallback is possible
if auth_param[:basic_auth_promt] && result[:auth] == false
-
return request_http_basic_authentication
end
diff --git a/app/controllers/integration/sipgate_controller.rb b/app/controllers/integration/sipgate_controller.rb
index 450317584..0cbf6f518 100644
--- a/app/controllers/integration/sipgate_controller.rb
+++ b/app/controllers/integration/sipgate_controller.rb
@@ -1,54 +1,60 @@
+# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
+
require 'builder'
class Integration::SipgateController < ApplicationController
- before_action { http_log_config facility: 'sipgate.io' }
+
+ # list current caller log
+ def index
+ return if !authentication_check
+ return if deny_if_not_role('CTI')
+ list = Cti::Log.order('created_at DESC').limit(60)
+ render json: list
+ end
# notify about inbound call / block inbound call
def in
+ http_log_config facility: 'sipgate.io'
return if !configured?
- config = Setting.get('sipgate_config')
- config_inbound = config[:inbound] || {}
- block_caller_ids = config_inbound[:block_caller_ids] || []
-
if params['event'] == 'newCall'
+ config = Setting.get('sipgate_config')
+ config_inbound = config[:inbound] || {}
+ block_caller_ids = config_inbound[:block_caller_ids] || []
+
# check if call need to be blocked
block_caller_ids.each {|item|
next unless item[:caller_id] == params['from']
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
- content = xml.Response('onHangup' => in_url, 'onAnswer' => in_url) do
+ content = xml.Response(onHangup: url, onAnswer: url) do
xml.Reject('reason' => 'busy')
end
send_data content, type: 'application/xml; charset=UTF-8;'
- params['Reject'] = 'busy'
- Sessions.broadcast(
- event: 'sipgate.io',
- data: params
- )
+ #params['Reject'] = 'busy'
+ params['comment'] = 'reject, busy'
+ if params['user']
+ params['comment'] = "#{params['user']} -> reject, busy"
+ end
+ update_log(params)
return true
}
end
+ update_log(params)
+
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
- content = xml.Response('onHangup' => in_url, 'onAnswer' => in_url)
-
+ content = xml.Response(onHangup: url, onAnswer: url)
send_data content, type: 'application/xml; charset=UTF-8;'
-
- # search for caller
- Sessions.broadcast(
- event: 'sipgate.io',
- data: params
- )
-
end
# set caller id of outbound call
def out
+ http_log_config facility: 'sipgate.io'
return if !configured?
config = Setting.get('sipgate_config')
@@ -61,31 +67,32 @@ class Integration::SipgateController < ApplicationController
# set callerId
content = nil
to = params[:to]
+ from = nil
if to
config_outbound.each {|row|
dest = row[:dest].gsub(/\*/, '.+?')
next if to !~ /^#{dest}$/
- content = xml.Response('onHangup' => in_url, 'onAnswer' => in_url) do
- xml.Dial(callerId: row[:caller_id]) { xml.Number(params[:to]) }
+ from = row[:caller_id]
+ content = xml.Response(onHangup: url, onAnswer: url) do
+ xml.Dial(callerId: from) { xml.Number(params[:to]) }
end
break
}
if !content && default_caller_id
- content = xml.Response('onHangup' => in_url, 'onAnswer' => in_url) do
+ from = default_caller_id
+ content = xml.Response(onHangup: url, onAnswer: url) do
xml.Dial(callerId: default_caller_id) { xml.Number(params[:to]) }
end
end
else
- content = xml.Response('onHangup' => in_url, 'onAnswer' => in_url)
+ content = xml.Response(onHangup: url, onAnswer: url)
end
send_data content, type: 'application/xml; charset=UTF-8;'
-
- # notify about outbound call
- Sessions.broadcast(
- event: 'sipgate.io:out',
- data: params
- )
+ if from
+ params['from'] = from
+ end
+ update_log(params)
end
private
@@ -103,23 +110,69 @@ class Integration::SipgateController < ApplicationController
true
end
+ def update_log(params)
+
+ user = params['user']
+ if params['user'] && params['user'].class == Array
+ user = params['user'].join(', ')
+ end
+ from_comment = nil
+ to_comment = nil
+ if params['direction'] == 'in'
+ to_comment = user
+ else
+ from_comment = user
+ end
+ comment = nil
+ if params['cause']
+ comment = params['cause']
+ end
+
+ if params['event'] == 'newCall'
+ Cti::Log.create(
+ direction: params['direction'],
+ from: params['from'],
+ from_comment: from_comment,
+ to: params['to'],
+ to_comment: to_comment,
+ call_id: params['callId'],
+ comment: comment,
+ state: params['event'],
+ )
+ elsif params['event'] == 'answer'
+ log = Cti::Log.find_by(call_id: params['callId'])
+ raise "No such call_id #{params['callId']}" if !log
+ log.state = 'answer'
+ log.comment = comment
+ log.save
+ elsif params['event'] == 'hangup'
+ log = Cti::Log.find_by(call_id: params['callId'])
+ raise "No such call_id #{params['callId']}" if !log
+ log.state = 'hangup'
+ log.comment = comment
+ log.save
+ else
+ raise "Unknown event #{params['event']}"
+ end
+
+ end
+
def xml_error(error)
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
content = xml.Response() do
xml.Error(error)
end
- send_data content, type: 'application/xml; charset=UTF-8;', status: '500'
+ send_data content, type: 'application/xml; charset=UTF-8;', status: 422
end
def base_url
http_type = Setting.get('http_type')
fqdn = Setting.get('fqdn')
-
"#{http_type}://#{fqdn}/api/v1/sipgate"
end
- def in_url
- "#{base_url}/in"
+ def url
+ "#{base_url}/#{params['direction']}"
end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index a2c727aae..072fb7e0a 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -85,7 +85,7 @@ class UsersController < ApplicationController
group_ids = []
role_ids = []
if count <= 2
- Role.where(name: [ Z_ROLENAME_ADMIN, 'Agent', 'Chat']).each { |role|
+ Role.where(name: [ Z_ROLENAME_ADMIN, 'Agent', 'Chat', 'CTI']).each { |role|
role_ids.push role.id
}
Group.all().each { |group|
diff --git a/app/models/cti/log.rb b/app/models/cti/log.rb
new file mode 100644
index 000000000..bf97b138a
--- /dev/null
+++ b/app/models/cti/log.rb
@@ -0,0 +1,78 @@
+module Cti
+ class Log < ApplicationModel
+ self.table_name = 'cti_logs'
+
+ after_create :push_event, :push_caller_list
+ after_update :push_event, :push_caller_list
+ after_destroy :push_event, :push_caller_list
+
+=begin
+
+ Cti::Log.create(
+ direction: 'in',
+ from: '007',
+ from_comment: '',
+ to: '008',
+ to_comment: '',
+ call_id: '1',
+ comment: '',
+ state: 'newCall',
+ )
+
+ Cti::Log.create(
+ direction: 'in',
+ from: '007',
+ from_comment: '',
+ to: '008',
+ to_comment: '',
+ call_id: '2',
+ comment: '',
+ state: 'answer',
+ )
+
+ Cti::Log.create(
+ direction: 'in',
+ from: '009',
+ from_comment: '',
+ to: '010',
+ to_comment: '',
+ call_id: '3',
+ comment: '',
+ state: 'hangup',
+ )
+
+=end
+
+ def push_event
+ users = User.of_role('CTI')
+ users.each {|user|
+
+ # send notify about event
+ Sessions.send_to(
+ user.id,
+ {
+ event: 'cti_event',
+ data: self,
+ },
+ )
+ }
+ end
+
+ def push_caller_list
+ list = Cti::Log.order('created_at DESC').limit(60)
+
+ users = User.of_role('CTI')
+ users.each {|user|
+
+ # send notify on create/update/delete
+ Sessions.send_to(
+ user.id,
+ {
+ event: 'cti_list_push',
+ data: list,
+ },
+ )
+ }
+ end
+ end
+end
diff --git a/config/routes/integration_sipgate.rb b/config/routes/integration_sipgate.rb
index 04d4d8f91..d569898d2 100644
--- a/config/routes/integration_sipgate.rb
+++ b/config/routes/integration_sipgate.rb
@@ -1,5 +1,6 @@
Zammad::Application.routes.draw do
+ match '/api/v1/cti/log', to: 'integration/sipgate#index', via: :get
match '/api/v1/sipgate/in', to: 'integration/sipgate#in', via: :post
match '/api/v1/sipgate/out', to: 'integration/sipgate#out', via: :post
diff --git a/db/migrate/20160422000003_create_cti_log.rb b/db/migrate/20160422000003_create_cti_log.rb
new file mode 100644
index 000000000..3bbfb0b61
--- /dev/null
+++ b/db/migrate/20160422000003_create_cti_log.rb
@@ -0,0 +1,26 @@
+class CreateCtiLog < ActiveRecord::Migration
+ def up
+ create_table :cti_logs do |t|
+ t.string :direction, limit: 20, null: false
+ t.string :state, limit: 20, null: false
+ t.string :from, limit: 100, null: false
+ t.string :from_comment, limit: 250, null: true
+ t.string :to, limit: 100, null: false
+ t.string :to_comment, limit: 250, null: true
+ t.string :call_id, limit: 250, null: false
+ t.string :comment, limit: 500, null: true
+ t.timestamps null: false
+ end
+ add_index :cti_logs, [:call_id], unique: true
+ add_index :cti_logs, [:direction]
+ add_index :cti_logs, [:from]
+
+ Role.create_if_not_exists(
+ name: 'CTI',
+ note: 'Access to CTI feature.',
+ updated_by_id: 1,
+ created_by_id: 1
+ )
+
+ end
+end
diff --git a/db/seeds.rb b/db/seeds.rb
index ae9c440ea..00a85194e 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -1915,6 +1915,13 @@ Role.create_if_not_exists(
updated_by_id: 1,
created_by_id: 1
)
+Role.create_if_not_exists(
+ id: 6,
+ name: 'CTI',
+ note: 'Access to CTI feature.',
+ updated_by_id: 1,
+ created_by_id: 1
+)
Group.create_if_not_exists(
id: 1,
diff --git a/test/integration/sipgate_controller_test.rb b/test/integration/sipgate_controller_test.rb
index fa9432433..98fb66ecc 100644
--- a/test/integration/sipgate_controller_test.rb
+++ b/test/integration/sipgate_controller_test.rb
@@ -5,6 +5,8 @@ require 'rexml/document'
class SipgateControllerTest < ActionDispatch::IntegrationTest
setup do
+ Cti::Log.destroy_all
+
Setting.create_or_update(
title: 'sipgate.io integration',
name: 'sipgate_integration',
@@ -65,12 +67,27 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
preferences: { prio: 2 },
)
+ groups = Group.where(name: 'Users')
+ roles = Role.where(name: 'Agent')
+ agent = User.create_or_update(
+ login: 'cti-agent@example.com',
+ firstname: 'E',
+ lastname: 'S',
+ email: 'cti-agent@example.com',
+ password: 'agentpw',
+ active: true,
+ roles: roles,
+ groups: groups,
+ updated_by_id: 1,
+ created_by_id: 1,
+ )
+
end
test 'basic call' do
# inbound - I
- params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278&user%5B%5D=user+1&user%5B%5D=user+2'
+ params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278-1&user%5B%5D=user+1&user%5B%5D=user+2'
post '/api/v1/sipgate/in', params
assert_response(200)
on_hangup = nil
@@ -85,7 +102,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('http://zammad.example.com/api/v1/sipgate/in', on_answer)
# inbound - II - block caller
- params = 'event=newCall&direction=in&from=491715000000&to=4930600000000&callId=4991155921769858278&user%5B%5D=user+1&user%5B%5D=user+2'
+ params = 'event=newCall&direction=in&from=491715000000&to=4930600000000&callId=4991155921769858278-2&user%5B%5D=user+1&user%5B%5D=user+2'
post '/api/v1/sipgate/in', params
assert_response(200)
on_hangup = nil
@@ -105,7 +122,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('busy', reason)
# outbound - I - set default_caller_id
- params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=8621106404543334274&user%5B%5D=user+1'
+ params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=8621106404543334274-3&user%5B%5D=user+1'
post '/api/v1/sipgate/out', params
assert_response(200)
on_hangup = nil
@@ -126,11 +143,11 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
end
assert_equal('4930777000000', caller_id)
assert_equal('4912347114711', number_to_dail)
- assert_equal('http://zammad.example.com/api/v1/sipgate/in', on_hangup)
- assert_equal('http://zammad.example.com/api/v1/sipgate/in', on_answer)
+ assert_equal('http://zammad.example.com/api/v1/sipgate/out', on_hangup)
+ assert_equal('http://zammad.example.com/api/v1/sipgate/out', on_answer)
# outbound - II - set caller_id based on routing_table by explicite number
- params = 'event=newCall&direction=out&from=4930600000000&to=491714000000&callId=8621106404543334274&user%5B%5D=user+1'
+ params = 'event=newCall&direction=out&from=4930600000000&to=491714000000&callId=8621106404543334274-4&user%5B%5D=user+1'
post '/api/v1/sipgate/out', params
assert_response(200)
on_hangup = nil
@@ -151,11 +168,11 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
end
assert_equal('41715880339000', caller_id)
assert_equal('491714000000', number_to_dail)
- assert_equal('http://zammad.example.com/api/v1/sipgate/in', on_hangup)
- assert_equal('http://zammad.example.com/api/v1/sipgate/in', on_answer)
+ assert_equal('http://zammad.example.com/api/v1/sipgate/out', on_hangup)
+ assert_equal('http://zammad.example.com/api/v1/sipgate/out', on_answer)
# outbound - III - set caller_id based on routing_table by 41*
- params = 'event=newCall&direction=out&from=4930600000000&to=4147110000000&callId=8621106404543334274&user%5B%5D=user+1'
+ params = 'event=newCall&direction=out&from=4930600000000&to=4147110000000&callId=8621106404543334274-5&user%5B%5D=user+1'
post '/api/v1/sipgate/out', params
assert_response(200)
on_hangup = nil
@@ -176,14 +193,14 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
end
assert_equal('41715880339000', caller_id)
assert_equal('4147110000000', number_to_dail)
- assert_equal('http://zammad.example.com/api/v1/sipgate/in', on_hangup)
- assert_equal('http://zammad.example.com/api/v1/sipgate/in', on_answer)
+ assert_equal('http://zammad.example.com/api/v1/sipgate/out', on_hangup)
+ assert_equal('http://zammad.example.com/api/v1/sipgate/out', on_answer)
# no config
Setting.set('sipgate_config', {})
- params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278&user%5B%5D=user+1&user%5B%5D=user+2'
+ params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278-6&user%5B%5D=user+1&user%5B%5D=user+2'
post '/api/v1/sipgate/in', params
- assert_response(500)
+ assert_response(422)
error = nil
content = @response.body
response = REXML::Document.new(content)
@@ -194,4 +211,123 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
end
+ test 'log call' do
+
+ # outbound - I - new call
+ params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=1234567890-1&user%5B%5D=user+1'
+ post '/api/v1/sipgate/out', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-1')
+ assert(log)
+ assert_equal('4930777000000', log.from)
+ assert_equal('4912347114711', log.to)
+ assert_equal('out', log.direction)
+ assert_equal('user 1', log.from_comment)
+ assert_equal(nil, log.comment)
+ assert_equal('newCall', log.state)
+
+ # outbound - I - hangup by agent
+ params = 'event=hangup&direction=out&callId=1234567890-1&cause=cancel'
+ post '/api/v1/sipgate/out', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-1')
+ assert(log)
+ assert_equal('4930777000000', log.from)
+ assert_equal('4912347114711', log.to)
+ assert_equal('out', log.direction)
+ assert_equal('user 1', log.from_comment)
+ assert_equal('cancel', log.comment)
+ assert_equal('hangup', log.state)
+
+ # outbound - II - new call
+ params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=1234567890-2&user%5B%5D=user+1'
+ post '/api/v1/sipgate/out', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-2')
+ assert(log)
+ assert_equal('4930777000000', log.from)
+ assert_equal('4912347114711', log.to)
+ assert_equal('out', log.direction)
+ assert_equal('user 1', log.from_comment)
+ assert_equal(nil, log.comment)
+ assert_equal('newCall', log.state)
+
+ # outbound - II - answer by customer
+ params = 'event=answer&direction=out&callId=1234567890-2&from=4930600000000&to=4912347114711'
+ post '/api/v1/sipgate/out', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-2')
+ assert(log)
+ assert_equal('4930777000000', log.from)
+ assert_equal('4912347114711', log.to)
+ assert_equal('out', log.direction)
+ assert_equal('user 1', log.from_comment)
+ assert_equal(nil, log.comment)
+ assert_equal('answer', log.state)
+
+ # outbound - II - hangup by customer
+ params = 'event=hangup&direction=out&callId=1234567890-2&cause=normalClearing&from=4930600000000&to=4912347114711'
+ post '/api/v1/sipgate/out', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-2')
+ assert(log)
+ assert_equal('4930777000000', log.from)
+ assert_equal('4912347114711', log.to)
+ assert_equal('out', log.direction)
+ assert_equal('user 1', log.from_comment)
+ assert_equal('normalClearing', log.comment)
+ assert_equal('hangup', log.state)
+
+ # inbound - I - new call
+ params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-3&user%5B%5D=user+1'
+ post '/api/v1/sipgate/in', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-3')
+ assert(log)
+ assert_equal('4930600000000', log.to)
+ assert_equal('4912347114711', log.from)
+ assert_equal('in', log.direction)
+ assert_equal('user 1', log.to_comment)
+ assert_equal(nil, log.comment)
+ assert_equal('newCall', log.state)
+
+ # inbound - I - answer by customer
+ params = 'event=answer&direction=in&callId=1234567890-3&to=4930600000000&from=4912347114711'
+ post '/api/v1/sipgate/in', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-3')
+ assert(log)
+ assert_equal('4930600000000', log.to)
+ assert_equal('4912347114711', log.from)
+ assert_equal('in', log.direction)
+ assert_equal('user 1', log.to_comment)
+ assert_equal(nil, log.comment)
+ assert_equal('answer', log.state)
+
+ # inbound - I - hangup by customer
+ params = 'event=hangup&direction=in&callId=1234567890-3&cause=normalClearing&to=4930600000000&from=4912347114711'
+ post '/api/v1/sipgate/in', params
+ assert_response(200)
+ log = Cti::Log.find_by(call_id: '1234567890-3')
+ assert(log)
+ assert_equal('4930600000000', log.to)
+ assert_equal('4912347114711', log.from)
+ assert_equal('in', log.direction)
+ assert_equal('user 1', log.to_comment)
+ assert_equal('normalClearing', log.comment)
+ assert_equal('hangup', log.state)
+
+ get '/api/v1/cti/log'
+ assert_response(401)
+
+ headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials('cti-agent@example.com', 'agentpw')
+ get '/api/v1/cti/log', {}, headers.merge('Authorization' => credentials)
+ assert_response(200)
+ result = JSON.parse(@response.body)
+ assert_equal(result.class, Array)
+ assert_equal(3, result.count)
+
+ end
+
end