punchblock 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/punchblock/client.rb +1 -1
- data/lib/punchblock/command/dial.rb +4 -1
- data/lib/punchblock/component/component_node.rb +14 -9
- data/lib/punchblock/connection/asterisk.rb +4 -0
- data/lib/punchblock/connection/xmpp.rb +4 -0
- data/lib/punchblock/core_ext/blather/stanza.rb +9 -1
- data/lib/punchblock/rayo_node.rb +3 -1
- data/lib/punchblock/translator/asterisk.rb +7 -3
- data/lib/punchblock/translator/asterisk/call.rb +16 -13
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +1 -1
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +1 -1
- data/spec/punchblock/client_spec.rb +7 -0
- data/spec/punchblock/command/dial_spec.rb +5 -2
- data/spec/punchblock/connection/asterisk_spec.rb +7 -0
- data/spec/punchblock/connection/xmpp_spec.rb +44 -0
- data/spec/punchblock/translator/asterisk/call_spec.rb +224 -0
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +23 -0
- data/spec/punchblock/translator/asterisk_spec.rb +38 -1
- data/spec/spec_helper.rb +4 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b59ef9a626a919b1db35af9603f8d5d73edae6a8
|
4
|
+
data.tar.gz: 47f018bf7cc1d9ba4e6d14acb0d4bc18e64622e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f143c3727b0bf9d205d3c9f8a09eec1eda96ad294af4dcca99b962de39c6aa47cce84ec24080a40ab4ca8ff78e244a444c66fa6571ba0e892871b475798549c
|
7
|
+
data.tar.gz: 55b015a502dc38e174c59707701046c0138ccf8e0db6afd318da38b7e7672a4271023cef5fca0f7b90bf22eaf6d2c695b6d5aee1a5acc0223a5147a410c9f77c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/punchblock)
|
2
2
|
|
3
|
+
# [v2.4.0](https://github.com/adhearsion/punchblock/compare/v2.3.1...v2.4.0) - [2014-03-01](https://rubygems.org/gems/punchblock/versions/2.4.0)
|
4
|
+
* Feature: Add support for requesting calls with a specific URI
|
5
|
+
* Feature: Allow generation of a random call URI for a client
|
6
|
+
* Feature: Rayo events should be timestamped with dispatch or receipt time (#213)
|
7
|
+
|
3
8
|
# [v2.3.1](https://github.com/adhearsion/punchblock/compare/v2.3.0...v2.3.1) - [2014-02-13](https://rubygems.org/gems/punchblock/versions/2.3.1)
|
4
9
|
* Bugfix: Ensure commands can be associated on the wire even before they're executed
|
5
10
|
* Bugfix: Ensure a command is always transitioned to is requested state prior to receiving a response
|
data/lib/punchblock/client.rb
CHANGED
@@ -10,7 +10,7 @@ module Punchblock
|
|
10
10
|
|
11
11
|
attr_reader :connection, :component_registry
|
12
12
|
|
13
|
-
delegate :run, :stop, :send_message, :to => :connection
|
13
|
+
delegate :run, :stop, :send_message, :new_call_uri, :to => :connection
|
14
14
|
|
15
15
|
# @param [Hash] options
|
16
16
|
# @option options [Connection::XMPP] :connection The Punchblock connection to use for this session
|
@@ -13,6 +13,9 @@ module Punchblock
|
|
13
13
|
# @return [String] the caller ID
|
14
14
|
attribute :from
|
15
15
|
|
16
|
+
# @return [String] the requested URI for the resulting call
|
17
|
+
attribute :uri
|
18
|
+
|
16
19
|
# @return [Integer] timeout in milliseconds
|
17
20
|
attribute :timeout, Integer
|
18
21
|
|
@@ -27,7 +30,7 @@ module Punchblock
|
|
27
30
|
end
|
28
31
|
|
29
32
|
def rayo_attributes
|
30
|
-
{to: to, from: from, timeout: timeout}
|
33
|
+
{to: to, from: from, uri: uri, timeout: timeout}
|
31
34
|
end
|
32
35
|
|
33
36
|
def rayo_children(root)
|
@@ -8,6 +8,7 @@ module Punchblock
|
|
8
8
|
def initialize(*args)
|
9
9
|
super
|
10
10
|
@complete_event_resource = FutureResource.new
|
11
|
+
@mutex = Mutex.new
|
11
12
|
register_internal_handlers
|
12
13
|
end
|
13
14
|
|
@@ -36,12 +37,14 @@ module Punchblock
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def response=(other)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
@mutex.synchronize do
|
41
|
+
if other.is_a?(Ref)
|
42
|
+
@component_id = other.component_id
|
43
|
+
@source_uri = other.uri.to_s
|
44
|
+
client.register_component self if client
|
45
|
+
end
|
46
|
+
super
|
43
47
|
end
|
44
|
-
super
|
45
48
|
end
|
46
49
|
|
47
50
|
def complete_event(timeout = nil)
|
@@ -49,10 +52,12 @@ module Punchblock
|
|
49
52
|
end
|
50
53
|
|
51
54
|
def complete_event=(other)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
@mutex.synchronize do
|
56
|
+
return if @complete_event_resource.set_yet?
|
57
|
+
client.delete_component_registration self if client
|
58
|
+
complete!
|
59
|
+
@complete_event_resource.resource = other
|
60
|
+
end
|
56
61
|
rescue StateMachine::InvalidTransition => e
|
57
62
|
e.message << " for component #{self}"
|
58
63
|
raise e
|
@@ -10,7 +10,7 @@ module Blather
|
|
10
10
|
def rayo_node
|
11
11
|
@rayo_node ||= begin
|
12
12
|
first_child = at_xpath RAYO_NODE_PATH, Punchblock::RAYO_NAMESPACES
|
13
|
-
Punchblock::RayoNode.from_xml first_child, nil, component_id, "xmpp:#{from}" if first_child
|
13
|
+
Punchblock::RayoNode.from_xml first_child, nil, component_id, "xmpp:#{from}", delay_timestamp if first_child
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -27,5 +27,13 @@ module Blather
|
|
27
27
|
def component_id
|
28
28
|
from.resource
|
29
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def delay_timestamp
|
34
|
+
if delay = self.at_xpath('ns:delay', ns: 'urn:xmpp:delay')
|
35
|
+
DateTime.parse(delay[:stamp])
|
36
|
+
end
|
37
|
+
end
|
30
38
|
end
|
31
39
|
end
|
data/lib/punchblock/rayo_node.rb
CHANGED
@@ -17,6 +17,7 @@ module Punchblock
|
|
17
17
|
attribute :source_uri
|
18
18
|
attribute :domain
|
19
19
|
attribute :transport
|
20
|
+
attribute :timestamp, DateTime, default: ->(*) { DateTime.now }
|
20
21
|
|
21
22
|
attr_accessor :connection, :client, :original_component
|
22
23
|
|
@@ -49,7 +50,7 @@ module Punchblock
|
|
49
50
|
# elements of the XML::Node
|
50
51
|
# @param [XML::Node] node the node to import
|
51
52
|
# @return the appropriate object based on the node name and namespace
|
52
|
-
def self.from_xml(node, call_id = nil, component_id = nil, uri = nil)
|
53
|
+
def self.from_xml(node, call_id = nil, component_id = nil, uri = nil, timestamp = nil)
|
53
54
|
ns = (node.namespace.href if node.namespace)
|
54
55
|
klass = class_from_registration(node.name, ns)
|
55
56
|
if klass && klass != self
|
@@ -60,6 +61,7 @@ module Punchblock
|
|
60
61
|
event.target_call_id = call_id
|
61
62
|
event.component_id = component_id
|
62
63
|
event.source_uri = uri
|
64
|
+
event.timestamp = timestamp if timestamp
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
@@ -140,9 +140,13 @@ module Punchblock
|
|
140
140
|
register_component component
|
141
141
|
component.execute
|
142
142
|
when Punchblock::Command::Dial
|
143
|
-
call =
|
144
|
-
|
145
|
-
|
143
|
+
if call = call_with_id(command.uri)
|
144
|
+
command.response = ProtocolError.new.setup(:conflict, 'Call ID already in use')
|
145
|
+
else
|
146
|
+
call = Call.new command.to, current_actor, ami_client, connection, nil, command.uri
|
147
|
+
register_call call
|
148
|
+
call.dial command
|
149
|
+
end
|
146
150
|
else
|
147
151
|
command.response = ProtocolError.new.setup 'command-not-acceptable', "Did not understand command"
|
148
152
|
end
|
@@ -25,10 +25,11 @@ module Punchblock
|
|
25
25
|
HANGUP_CAUSE_TO_END_REASON[22] = :reject
|
26
26
|
HANGUP_CAUSE_TO_END_REASON[102] = :timeout
|
27
27
|
|
28
|
-
def initialize(channel, translator, ami_client, connection, agi_env = nil)
|
28
|
+
def initialize(channel, translator, ami_client, connection, agi_env = nil, id = nil)
|
29
29
|
@channel, @translator, @ami_client, @connection = channel, translator, ami_client, connection
|
30
30
|
@agi_env = agi_env || {}
|
31
|
-
@id
|
31
|
+
@id = id || Punchblock.new_uuid
|
32
|
+
@components = {}
|
32
33
|
@answered = false
|
33
34
|
@pending_joins = {}
|
34
35
|
@progress_sent = false
|
@@ -113,7 +114,7 @@ module Punchblock
|
|
113
114
|
|
114
115
|
case ami_event.name
|
115
116
|
when 'Hangup'
|
116
|
-
handle_hangup_event ami_event['Cause'].to_i
|
117
|
+
handle_hangup_event ami_event['Cause'].to_i, ami_event.best_time
|
117
118
|
when 'AsyncAGI'
|
118
119
|
if component = component_with_id(ami_event['CommandID'])
|
119
120
|
component.handle_ami_event ami_event
|
@@ -121,16 +122,16 @@ module Punchblock
|
|
121
122
|
|
122
123
|
if @answered == false && ami_event['SubEvent'] == 'Start'
|
123
124
|
@answered = true
|
124
|
-
send_pb_event Event::Answered.new
|
125
|
+
send_pb_event Event::Answered.new(timestamp: ami_event.best_time)
|
125
126
|
end
|
126
127
|
when 'Newstate'
|
127
128
|
case ami_event['ChannelState']
|
128
129
|
when '5'
|
129
|
-
send_pb_event Event::Ringing.new
|
130
|
+
send_pb_event Event::Ringing.new(timestamp: ami_event.best_time)
|
130
131
|
end
|
131
132
|
when 'OriginateResponse'
|
132
133
|
if ami_event['Response'] == 'Failure' && ami_event['Uniqueid'] == '<null>'
|
133
|
-
send_end_event :error
|
134
|
+
send_end_event :error, nil, ami_event.best_time
|
134
135
|
end
|
135
136
|
when 'BridgeExec'
|
136
137
|
join_command = @pending_joins.delete ami_event['Channel1']
|
@@ -141,16 +142,16 @@ module Punchblock
|
|
141
142
|
if other_call = translator.call_for_channel(other_call_channel)
|
142
143
|
event = case ami_event['Bridgestate']
|
143
144
|
when 'Link'
|
144
|
-
Event::Joined.new call_uri: other_call.id
|
145
|
+
Event::Joined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
145
146
|
when 'Unlink'
|
146
|
-
Event::Unjoined.new call_uri: other_call.id
|
147
|
+
Event::Unjoined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
147
148
|
end
|
148
149
|
send_pb_event event
|
149
150
|
end
|
150
151
|
when 'Unlink'
|
151
152
|
other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first
|
152
153
|
if other_call = translator.call_for_channel(other_call_channel)
|
153
|
-
send_pb_event Event::Unjoined.new(call_uri: other_call.id)
|
154
|
+
send_pb_event Event::Unjoined.new(call_uri: other_call.id, timestamp: ami_event.best_time)
|
154
155
|
end
|
155
156
|
when 'VarSet'
|
156
157
|
@channel_variables[ami_event['Variable']] = ami_event['Value']
|
@@ -278,13 +279,14 @@ module Punchblock
|
|
278
279
|
send_ami_action 'Redirect', redirect_options
|
279
280
|
end
|
280
281
|
|
281
|
-
def handle_hangup_event(code =
|
282
|
+
def handle_hangup_event(code = nil, timestamp = nil)
|
283
|
+
code ||= 16
|
282
284
|
reason = @hangup_cause || HANGUP_CAUSE_TO_END_REASON[code]
|
283
285
|
@block_commands = true
|
284
286
|
@components.each_pair do |id, component|
|
285
287
|
component.call_ended
|
286
288
|
end
|
287
|
-
send_end_event reason, code
|
289
|
+
send_end_event reason, code, timestamp
|
288
290
|
end
|
289
291
|
|
290
292
|
def after(*args, &block)
|
@@ -306,8 +308,9 @@ module Punchblock
|
|
306
308
|
AMIErrorConverter.convert { @ami_client.send_action name, headers }
|
307
309
|
end
|
308
310
|
|
309
|
-
def send_end_event(reason, code = nil)
|
310
|
-
|
311
|
+
def send_end_event(reason, code = nil, timestamp = nil)
|
312
|
+
end_event = Event::End.new(reason: reason, platform_code: code, timestamp: timestamp)
|
313
|
+
send_pb_event end_event
|
311
314
|
translator.deregister_call id, channel
|
312
315
|
end
|
313
316
|
|
@@ -23,7 +23,7 @@ module Punchblock
|
|
23
23
|
if event.name == 'AsyncAGI' && event['SubEvent'] == 'Exec'
|
24
24
|
send_complete_event success_reason(event)
|
25
25
|
if @component_node.name == 'ASYNCAGI BREAK' && @call.channel_var('PUNCHBLOCK_END_ON_ASYNCAGI_BREAK')
|
26
|
-
@call.handle_hangup_event
|
26
|
+
@call.handle_hangup_event nil, event.best_time
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
data/lib/punchblock/version.rb
CHANGED
data/punchblock.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.add_runtime_dependency %q<future-resource>, ["~> 1.0"]
|
30
30
|
s.add_runtime_dependency %q<has-guarded-handlers>, ["~> 1.5"]
|
31
31
|
s.add_runtime_dependency %q<celluloid>, ["~> 0.14"]
|
32
|
-
s.add_runtime_dependency %q<ruby_ami>, ["~> 2.
|
32
|
+
s.add_runtime_dependency %q<ruby_ami>, ["~> 2.2"]
|
33
33
|
s.add_runtime_dependency %q<ruby_fs>, ["~> 1.1"]
|
34
34
|
s.add_runtime_dependency %q<ruby_speech>, ["~> 2.3"]
|
35
35
|
s.add_runtime_dependency %q<virtus>, ["~> 1.0"]
|
@@ -40,6 +40,13 @@ module Punchblock
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
describe '#new_call_uri' do
|
44
|
+
it 'should return the connection-specific fresh call ID' do
|
45
|
+
stub_uuids 'foobar'
|
46
|
+
subject.new_call_uri.should == 'xmpp:foobar@call.rayo.net'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
43
50
|
it 'should handle connection events' do
|
44
51
|
subject.should_receive(:handle_event).with(mock_event).once
|
45
52
|
connection.event_handler.call mock_event
|
@@ -13,10 +13,11 @@ module Punchblock
|
|
13
13
|
let(:join_params) { {:call_uri => 'abc123'} }
|
14
14
|
|
15
15
|
describe "when setting options in initializer" do
|
16
|
-
subject { described_class.new to: 'tel:+14155551212', from: 'tel:+13035551212', timeout: 30000, headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' }, join: join_params }
|
16
|
+
subject { described_class.new to: 'tel:+14155551212', from: 'tel:+13035551212', uri: 'xmpp:foo@bar.com', timeout: 30000, headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' }, join: join_params }
|
17
17
|
|
18
18
|
its(:to) { should be == 'tel:+14155551212' }
|
19
19
|
its(:from) { should be == 'tel:+13035551212' }
|
20
|
+
its(:uri) { should be == 'xmpp:foo@bar.com' }
|
20
21
|
its(:timeout) { should be == 30000 }
|
21
22
|
its(:join) { should be == Join.new(join_params) }
|
22
23
|
its(:headers) { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
@@ -27,6 +28,7 @@ module Punchblock
|
|
27
28
|
new_instance.should be_instance_of described_class
|
28
29
|
new_instance.to.should == 'tel:+14155551212'
|
29
30
|
new_instance.from.should == 'tel:+13035551212'
|
31
|
+
new_instance.uri.should == 'xmpp:foo@bar.com'
|
30
32
|
new_instance.timeout.should == 30000
|
31
33
|
new_instance.join.should == Join.new(join_params)
|
32
34
|
new_instance.headers.should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' }
|
@@ -54,7 +56,7 @@ module Punchblock
|
|
54
56
|
describe "from a stanza" do
|
55
57
|
let :stanza do
|
56
58
|
<<-MESSAGE
|
57
|
-
<dial to='tel:+14155551212' from='tel:+13035551212' timeout='30000' xmlns='urn:xmpp:rayo:1'>
|
59
|
+
<dial to='tel:+14155551212' from='tel:+13035551212' uri='xmpp:foo@bar.com' timeout='30000' xmlns='urn:xmpp:rayo:1'>
|
58
60
|
<join call-uri="abc123" />
|
59
61
|
<header name="X-skill" value="agent" />
|
60
62
|
<header name="X-customer-id" value="8877" />
|
@@ -68,6 +70,7 @@ module Punchblock
|
|
68
70
|
|
69
71
|
its(:to) { should be == 'tel:+14155551212' }
|
70
72
|
its(:from) { should be == 'tel:+13035551212' }
|
73
|
+
its(:uri) { should be == 'xmpp:foo@bar.com' }
|
71
74
|
its(:timeout) { should be == 30000 }
|
72
75
|
its(:join) { should be == Join.new(join_params) }
|
73
76
|
its(:headers) { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
@@ -17,22 +17,42 @@ module Punchblock
|
|
17
17
|
subject { connection }
|
18
18
|
|
19
19
|
describe "rayo domains" do
|
20
|
+
before { stub_uuids 'randomcallid' }
|
21
|
+
|
20
22
|
context "with no domains specified, and a JID of 1@app.rayo.net" do
|
21
23
|
let(:options) { { :username => '1@app.rayo.net' } }
|
22
24
|
|
23
25
|
its(:root_domain) { should be == 'app.rayo.net' }
|
26
|
+
|
27
|
+
describe '#new_call_uri' do
|
28
|
+
it "should return an appropriate random call URI" do
|
29
|
+
subject.new_call_uri.should == 'xmpp:randomcallid@app.rayo.net'
|
30
|
+
end
|
31
|
+
end
|
24
32
|
end
|
25
33
|
|
26
34
|
context "with only a rayo domain set" do
|
27
35
|
let(:options) { { :rayo_domain => 'rayo.org' } }
|
28
36
|
|
29
37
|
its(:root_domain) { should be == 'rayo.org' }
|
38
|
+
|
39
|
+
describe '#new_call_uri' do
|
40
|
+
it "should return an appropriate random call URI" do
|
41
|
+
subject.new_call_uri.should == 'xmpp:randomcallid@rayo.org'
|
42
|
+
end
|
43
|
+
end
|
30
44
|
end
|
31
45
|
|
32
46
|
context "with only a root domain set" do
|
33
47
|
let(:options) { { :root_domain => 'rayo.org' } }
|
34
48
|
|
35
49
|
its(:root_domain) { should be == 'rayo.org' }
|
50
|
+
|
51
|
+
describe '#new_call_uri' do
|
52
|
+
it "should return an appropriate random call URI" do
|
53
|
+
subject.new_call_uri.should == 'xmpp:randomcallid@rayo.org'
|
54
|
+
end
|
55
|
+
end
|
36
56
|
end
|
37
57
|
end
|
38
58
|
|
@@ -217,6 +237,11 @@ module Punchblock
|
|
217
237
|
MSG
|
218
238
|
end
|
219
239
|
|
240
|
+
before do
|
241
|
+
@now = DateTime.now
|
242
|
+
DateTime.stub now: @now
|
243
|
+
end
|
244
|
+
|
220
245
|
let(:example_event) { import_stanza offer_xml }
|
221
246
|
|
222
247
|
it { example_event.should be_a Blather::Stanza::Presence }
|
@@ -228,9 +253,28 @@ module Punchblock
|
|
228
253
|
event.source_uri.should be == 'xmpp:9f00061@call.rayo.net'
|
229
254
|
event.domain.should be == 'call.rayo.net'
|
230
255
|
event.transport.should be == 'xmpp'
|
256
|
+
event.timestamp.should be == @now
|
231
257
|
end
|
232
258
|
handle_presence
|
233
259
|
end
|
260
|
+
|
261
|
+
context "with a delayed delivery timestamp" do
|
262
|
+
let :offer_xml do
|
263
|
+
<<-MSG
|
264
|
+
<presence to='16577@app.rayo.net/1' from='9f00061@call.rayo.net'>
|
265
|
+
<offer xmlns="urn:xmpp:rayo:1" to="sip:whatever@127.0.0.1" from="sip:ylcaomxb@192.168.1.9"/>
|
266
|
+
<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25Z'/>
|
267
|
+
</presence>
|
268
|
+
MSG
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should stamp that time on the rayo event' do
|
272
|
+
mock_event_handler.should_receive(:call).once.with do |event|
|
273
|
+
event.timestamp.should be == DateTime.new(2002, 9, 10, 23, 8, 25, 0)
|
274
|
+
end
|
275
|
+
handle_presence
|
276
|
+
end
|
277
|
+
end
|
234
278
|
end
|
235
279
|
|
236
280
|
describe "from something that's not a real event" do
|
@@ -327,6 +327,27 @@ module Punchblock
|
|
327
327
|
comp_command.response(0.1).should == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{call_id}", call_id)
|
328
328
|
end
|
329
329
|
|
330
|
+
context "when the AMI event has a timestamp" do
|
331
|
+
let :ami_event do
|
332
|
+
RubyAMI::Event.new 'Hangup',
|
333
|
+
'Uniqueid' => "1320842458.8",
|
334
|
+
'Cause' => cause,
|
335
|
+
'Cause-txt' => cause_txt,
|
336
|
+
'Channel' => "SIP/1234-00000000",
|
337
|
+
'Timestamp' => '1393368380.572575'
|
338
|
+
end
|
339
|
+
|
340
|
+
it "should use the AMI timestamp for the Rayo event" do
|
341
|
+
expected_end_event = Punchblock::Event::End.new reason: :hangup,
|
342
|
+
platform_code: cause,
|
343
|
+
target_call_id: subject.id,
|
344
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
345
|
+
translator.should_receive(:handle_pb_event).with expected_end_event
|
346
|
+
|
347
|
+
subject.process_ami_event ami_event
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
330
351
|
context "after processing a hangup command" do
|
331
352
|
let(:command) { Command::Hangup.new }
|
332
353
|
|
@@ -534,6 +555,24 @@ module Punchblock
|
|
534
555
|
subject.process_ami_event ami_event
|
535
556
|
end
|
536
557
|
end
|
558
|
+
|
559
|
+
context "when the AMI event has a timestamp" do
|
560
|
+
let :ami_event do
|
561
|
+
RubyAMI::Event.new "AsyncAGI",
|
562
|
+
"SubEvent" => "Start",
|
563
|
+
"Channel" => "SIP/1234-00000000",
|
564
|
+
"Env" => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2Fuserb-00000006%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201390303636.6%0Aagi_version%3A%2011.7.0%0Aagi_callerid%3A%20userb%0Aagi_calleridname%3A%20User%20B%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%20unknown%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20adhearsion-redirect%0Aagi_extension%3A%201%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%20139696536876800%0A%0A",
|
565
|
+
'Timestamp' => '1393368380.572575'
|
566
|
+
end
|
567
|
+
|
568
|
+
it "should use the AMI timestamp for the Rayo event" do
|
569
|
+
expected_answered = Punchblock::Event::Answered.new target_call_id: subject.id,
|
570
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
571
|
+
translator.should_receive(:handle_pb_event).with expected_answered
|
572
|
+
|
573
|
+
subject.process_ami_event ami_event
|
574
|
+
end
|
575
|
+
end
|
537
576
|
end
|
538
577
|
|
539
578
|
context 'with a Newstate event' do
|
@@ -565,6 +604,25 @@ module Punchblock
|
|
565
604
|
subject.process_ami_event ami_event
|
566
605
|
subject.answered?.should be_false
|
567
606
|
end
|
607
|
+
|
608
|
+
context "when the AMI event has a timestamp" do
|
609
|
+
let :ami_event do
|
610
|
+
RubyAMI::Event.new 'Newstate',
|
611
|
+
'Channel' => 'SIP/1234-00000000',
|
612
|
+
'ChannelState' => channel_state,
|
613
|
+
'ChannelStateDesc' => channel_state_desc,
|
614
|
+
'Uniqueid' => '1326194671.0',
|
615
|
+
'Timestamp' => '1393368380.572575'
|
616
|
+
end
|
617
|
+
|
618
|
+
it "should use the AMI timestamp for the Rayo event" do
|
619
|
+
expected_ringing = Punchblock::Event::Ringing.new target_call_id: subject.id,
|
620
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
621
|
+
translator.should_receive(:handle_pb_event).with expected_ringing
|
622
|
+
|
623
|
+
subject.process_ami_event ami_event
|
624
|
+
end
|
625
|
+
end
|
568
626
|
end
|
569
627
|
end
|
570
628
|
|
@@ -613,6 +671,32 @@ module Punchblock
|
|
613
671
|
translator.should_receive(:handle_pb_event).with expected_end_event
|
614
672
|
subject.process_ami_event ami_event
|
615
673
|
end
|
674
|
+
|
675
|
+
context "when the AMI event has a timestamp" do
|
676
|
+
let :ami_event do
|
677
|
+
RubyAMI::Event.new 'OriginateResponse',
|
678
|
+
'Privilege' => 'call,all',
|
679
|
+
'ActionID' => '9d0c1aa4-5e3b-4cae-8aef-76a6119e2909',
|
680
|
+
'Response' => response,
|
681
|
+
'Channel' => 'SIP/15557654321',
|
682
|
+
'Context' => '',
|
683
|
+
'Exten' => '',
|
684
|
+
'Reason' => '0',
|
685
|
+
'Uniqueid' => uniqueid,
|
686
|
+
'CallerIDNum' => 'sip:5551234567',
|
687
|
+
'CallerIDName' => 'Bryan 100',
|
688
|
+
'Timestamp' => '1393368380.572575'
|
689
|
+
end
|
690
|
+
|
691
|
+
it "should use the AMI timestamp for the Rayo event" do
|
692
|
+
expected_end_event = Punchblock::Event::End.new reason: :error,
|
693
|
+
target_call_id: subject.id,
|
694
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
695
|
+
translator.should_receive(:handle_pb_event).with expected_end_event
|
696
|
+
|
697
|
+
subject.process_ami_event ami_event
|
698
|
+
end
|
699
|
+
end
|
616
700
|
end
|
617
701
|
end
|
618
702
|
|
@@ -749,6 +833,54 @@ module Punchblock
|
|
749
833
|
translator.should_receive(:handle_pb_event).with expected_joined
|
750
834
|
subject.process_ami_event switched_ami_event
|
751
835
|
end
|
836
|
+
|
837
|
+
context "when the AMI event has a timestamp" do
|
838
|
+
let :ami_event do
|
839
|
+
RubyAMI::Event.new 'Bridge',
|
840
|
+
'Privilege' => "call,all",
|
841
|
+
'Bridgestate' => state,
|
842
|
+
'Bridgetype' => "core",
|
843
|
+
'Channel1' => channel,
|
844
|
+
'Channel2' => other_channel,
|
845
|
+
'Uniqueid1' => "1319717537.11",
|
846
|
+
'Uniqueid2' => "1319717537.10",
|
847
|
+
'CallerID1' => "1234",
|
848
|
+
'CallerID2' => "5678",
|
849
|
+
'Timestamp' => '1393368380.572575'
|
850
|
+
end
|
851
|
+
|
852
|
+
let :switched_ami_event do
|
853
|
+
RubyAMI::Event.new 'Bridge',
|
854
|
+
'Privilege' => "call,all",
|
855
|
+
'Bridgestate' => state,
|
856
|
+
'Bridgetype' => "core",
|
857
|
+
'Channel1' => other_channel,
|
858
|
+
'Channel2' => channel,
|
859
|
+
'Uniqueid1' => "1319717537.11",
|
860
|
+
'Uniqueid2' => "1319717537.10",
|
861
|
+
'CallerID1' => "1234",
|
862
|
+
'CallerID2' => "5678",
|
863
|
+
'Timestamp' => '1393368380.572575'
|
864
|
+
end
|
865
|
+
|
866
|
+
before { expected_joined.timestamp = DateTime.new(2014, 2, 25, 22, 46, 20) }
|
867
|
+
|
868
|
+
context "when the call is the first channel" do
|
869
|
+
it "should use the AMI timestamp for the Rayo event" do
|
870
|
+
translator.should_receive(:handle_pb_event).with expected_joined
|
871
|
+
|
872
|
+
subject.process_ami_event ami_event
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
context "when the call is the second channel" do
|
877
|
+
it "should use the AMI timestamp for the Rayo event" do
|
878
|
+
translator.should_receive(:handle_pb_event).with expected_joined
|
879
|
+
|
880
|
+
subject.process_ami_event switched_ami_event
|
881
|
+
end
|
882
|
+
end
|
883
|
+
end
|
752
884
|
end
|
753
885
|
|
754
886
|
context "of state 'Unlink'" do
|
@@ -768,6 +900,54 @@ module Punchblock
|
|
768
900
|
translator.should_receive(:handle_pb_event).with expected_unjoined
|
769
901
|
subject.process_ami_event switched_ami_event
|
770
902
|
end
|
903
|
+
|
904
|
+
context "when the AMI event has a timestamp" do
|
905
|
+
let :ami_event do
|
906
|
+
RubyAMI::Event.new 'Bridge',
|
907
|
+
'Privilege' => "call,all",
|
908
|
+
'Bridgestate' => state,
|
909
|
+
'Bridgetype' => "core",
|
910
|
+
'Channel1' => channel,
|
911
|
+
'Channel2' => other_channel,
|
912
|
+
'Uniqueid1' => "1319717537.11",
|
913
|
+
'Uniqueid2' => "1319717537.10",
|
914
|
+
'CallerID1' => "1234",
|
915
|
+
'CallerID2' => "5678",
|
916
|
+
'Timestamp' => '1393368380.572575'
|
917
|
+
end
|
918
|
+
|
919
|
+
let :switched_ami_event do
|
920
|
+
RubyAMI::Event.new 'Bridge',
|
921
|
+
'Privilege' => "call,all",
|
922
|
+
'Bridgestate' => state,
|
923
|
+
'Bridgetype' => "core",
|
924
|
+
'Channel1' => other_channel,
|
925
|
+
'Channel2' => channel,
|
926
|
+
'Uniqueid1' => "1319717537.11",
|
927
|
+
'Uniqueid2' => "1319717537.10",
|
928
|
+
'CallerID1' => "1234",
|
929
|
+
'CallerID2' => "5678",
|
930
|
+
'Timestamp' => '1393368380.572575'
|
931
|
+
end
|
932
|
+
|
933
|
+
before { expected_unjoined.timestamp = DateTime.new(2014, 2, 25, 22, 46, 20) }
|
934
|
+
|
935
|
+
context "when the call is the first channel" do
|
936
|
+
it "should use the AMI timestamp for the Rayo event" do
|
937
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
938
|
+
|
939
|
+
subject.process_ami_event ami_event
|
940
|
+
end
|
941
|
+
end
|
942
|
+
|
943
|
+
context "when the call is the second channel" do
|
944
|
+
it "should use the AMI timestamp for the Rayo event" do
|
945
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
946
|
+
|
947
|
+
subject.process_ami_event switched_ami_event
|
948
|
+
end
|
949
|
+
end
|
950
|
+
end
|
771
951
|
end
|
772
952
|
end
|
773
953
|
|
@@ -820,6 +1000,50 @@ module Punchblock
|
|
820
1000
|
translator.should_receive(:handle_pb_event).with expected_unjoined
|
821
1001
|
subject.process_ami_event switched_ami_event
|
822
1002
|
end
|
1003
|
+
|
1004
|
+
context "when the AMI event has a timestamp" do
|
1005
|
+
let :ami_event do
|
1006
|
+
RubyAMI::Event.new 'Unlink',
|
1007
|
+
'Privilege' => "call,all",
|
1008
|
+
'Channel1' => channel,
|
1009
|
+
'Channel2' => other_channel,
|
1010
|
+
'Uniqueid1' => "1319717537.11",
|
1011
|
+
'Uniqueid2' => "1319717537.10",
|
1012
|
+
'CallerID1' => "1234",
|
1013
|
+
'CallerID2' => "5678",
|
1014
|
+
'Timestamp' => '1393368380.572575'
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
let :switched_ami_event do
|
1018
|
+
RubyAMI::Event.new 'Unlink',
|
1019
|
+
'Privilege' => "call,all",
|
1020
|
+
'Channel1' => other_channel,
|
1021
|
+
'Channel2' => channel,
|
1022
|
+
'Uniqueid1' => "1319717537.11",
|
1023
|
+
'Uniqueid2' => "1319717537.10",
|
1024
|
+
'CallerID1' => "1234",
|
1025
|
+
'CallerID2' => "5678",
|
1026
|
+
'Timestamp' => '1393368380.572575'
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
before { expected_unjoined.timestamp = DateTime.new(2014, 2, 25, 22, 46, 20) }
|
1030
|
+
|
1031
|
+
context "when the call is the first channel" do
|
1032
|
+
it "should use the AMI timestamp for the Rayo event" do
|
1033
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
1034
|
+
|
1035
|
+
subject.process_ami_event ami_event
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
context "when the call is the second channel" do
|
1040
|
+
it "should use the AMI timestamp for the Rayo event" do
|
1041
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
1042
|
+
|
1043
|
+
subject.process_ami_event switched_ami_event
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
end
|
823
1047
|
end
|
824
1048
|
|
825
1049
|
context 'with a VarSet event' do
|
@@ -168,6 +168,29 @@ module Punchblock
|
|
168
168
|
translator.should_receive(:handle_pb_event).once.with expected_end_event
|
169
169
|
subject.handle_ami_event ami_event
|
170
170
|
end
|
171
|
+
|
172
|
+
context "when the AMI event has a timestamp" do
|
173
|
+
let :ami_event do
|
174
|
+
RubyAMI::Event.new 'AsyncAGI',
|
175
|
+
"SubEvent" => "Exec",
|
176
|
+
"Channel" => channel,
|
177
|
+
"CommandId" => component_id,
|
178
|
+
"Command" => "EXEC ANSWER",
|
179
|
+
"Result" => "200%20result=123%20(timeout)%0A",
|
180
|
+
'Timestamp' => '1393368380.572575'
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should use the AMI timestamp for the Rayo event" do
|
184
|
+
expected_end_event = Punchblock::Event::End.new reason: :hangup,
|
185
|
+
platform_code: 16,
|
186
|
+
target_call_id: mock_call.id,
|
187
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
188
|
+
translator.should_receive(:handle_pb_event).once.with kind_of(Punchblock::Event::Complete)
|
189
|
+
translator.should_receive(:handle_pb_event).once.with expected_end_event
|
190
|
+
|
191
|
+
subject.handle_ami_event ami_event
|
192
|
+
end
|
193
|
+
end
|
171
194
|
end
|
172
195
|
end
|
173
196
|
end
|
@@ -7,7 +7,7 @@ module Punchblock
|
|
7
7
|
module Translator
|
8
8
|
describe Asterisk do
|
9
9
|
let(:ami_client) { double 'RubyAMI::Client' }
|
10
|
-
let(:connection) {
|
10
|
+
let(:connection) { Connection::Asterisk.new }
|
11
11
|
|
12
12
|
let(:translator) { Asterisk.new ami_client, connection }
|
13
13
|
|
@@ -16,6 +16,10 @@ module Punchblock
|
|
16
16
|
its(:ami_client) { should be ami_client }
|
17
17
|
its(:connection) { should be connection }
|
18
18
|
|
19
|
+
before do
|
20
|
+
connection.event_handler = ->(*) {}
|
21
|
+
end
|
22
|
+
|
19
23
|
after { translator.terminate if translator.alive? }
|
20
24
|
|
21
25
|
describe '#execute_command' do
|
@@ -210,6 +214,39 @@ module Punchblock
|
|
210
214
|
mock_call.should_receive(:dial).once.with command
|
211
215
|
subject.execute_global_command command
|
212
216
|
end
|
217
|
+
|
218
|
+
context 'when requesting a specific URI' do
|
219
|
+
let(:requested_uri) { connection.new_call_uri }
|
220
|
+
|
221
|
+
before do
|
222
|
+
command.uri = requested_uri
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should assign the requested URI to the call" do
|
226
|
+
subject.execute_global_command command
|
227
|
+
subject.call_with_id(requested_uri).id.should == requested_uri
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'and the requested URI already represents a known call' do
|
231
|
+
before do
|
232
|
+
earlier_command = Command::Dial.new to: 'SIP/1234', uri: requested_uri
|
233
|
+
earlier_command.request!
|
234
|
+
|
235
|
+
subject.execute_global_command earlier_command
|
236
|
+
|
237
|
+
@first_call = subject.call_with_id(requested_uri)
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should set the command response to a conflict error" do
|
241
|
+
subject.execute_global_command command
|
242
|
+
command.response(0.1).should == ProtocolError.new.setup(:conflict, 'Call ID already in use')
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should not replace the original call in the registry" do
|
246
|
+
subject.call_with_id(requested_uri).should be @first_call
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
213
250
|
end
|
214
251
|
|
215
252
|
context 'with an AMI action' do
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,7 @@ require 'countdownlatch'
|
|
5
5
|
require 'logger'
|
6
6
|
require 'celluloid'
|
7
7
|
require 'coveralls'
|
8
|
+
require 'ruby_ami'
|
8
9
|
Coveralls.wear!
|
9
10
|
|
10
11
|
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
@@ -27,6 +28,9 @@ RSpec.configure do |config|
|
|
27
28
|
config.before do
|
28
29
|
@uuid = SecureRandom.uuid
|
29
30
|
Punchblock.stub new_request_id: @uuid
|
31
|
+
|
32
|
+
@current_datetime = DateTime.now
|
33
|
+
DateTime.stub now: @current_datetime
|
30
34
|
end
|
31
35
|
|
32
36
|
config.after :each do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: punchblock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Goecke
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-03-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: nokogiri
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '2.
|
131
|
+
version: '2.2'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '2.
|
138
|
+
version: '2.2'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: ruby_fs
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|