electric_slide 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/build.yml +27 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +8 -0
- data/electric_slide.gemspec +6 -2
- data/lib/electric_slide/agent.rb +21 -0
- data/lib/electric_slide/call_queue.rb +42 -15
- data/lib/electric_slide/version.rb +1 -1
- data/spec/electric_slide/agent_spec.rb +52 -0
- data/spec/electric_slide/call_queue_spec.rb +60 -0
- metadata +44 -11
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c782d5d738f3fb714ff7fb1f3ae8ab63189118ebee98136ceccd9f1d41790526
|
4
|
+
data.tar.gz: 253786fe5964259c43a90d0dc2132df9bf1509d75203276f9ef6c6cdfc736eb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc24e7ae491fd5d7f3ce5925695d16c6ea9fcb0da4e49ca8258247289a498efcdd17c98a747740c813e8df82dfe368a15ae915b3df19e7b53c404a60b5614e8e
|
7
|
+
data.tar.gz: 474108814ec1a811b56c6a8c435d457429a1a447c6901660d1f6dd457c56cc00c4ef2b226e85b19c40fdf90e8ccd93d81243b1c78aea9551c635194c183a19fa
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: build
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
ruby: ['2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', 'jruby-9.1.17', 'jruby-9.2.21']
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- name: Checkout
|
14
|
+
uses: actions/checkout@v3
|
15
|
+
|
16
|
+
- name: Setup Ruby
|
17
|
+
uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: ${{ matrix.ruby }}
|
20
|
+
bundler: 1.17.3
|
21
|
+
bundler-cache: true
|
22
|
+
|
23
|
+
- name: Create build
|
24
|
+
run: bundle exec rake build
|
25
|
+
|
26
|
+
- name: Run Specs
|
27
|
+
run: bundle exec rake spec
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/electric_slide)
|
2
|
+
* Bugfix: Conditionally clear agent's call object if the current
|
3
|
+
call is the same as the previous call so that call object information
|
4
|
+
is not lost when the call goes from queued to connected
|
5
|
+
|
6
|
+
# [develop](https://github.com/adhearsion/electric_slide)
|
7
|
+
* Added `ElectricSlide::CallQueue#update_agent` to safely update a queued agent object's attributes
|
8
|
+
* Added `ElectricSlide::Agent#update` to update an agent object's attributes
|
9
|
+
* Added `ElectricSlide::Agent#callable?` to check if an agent can be called
|
2
10
|
|
3
11
|
# [0.5.0](https://github.com/adhearsion/electric_slide/compare/v0.4.2...v0.5.0) - [2016-02-12](https://rubygems.org/gems/adhearsion/versions/0.5.0)
|
4
12
|
* API Breakage: Fixed priority strategy now ensures that the same agent cannot be in multiple priorities
|
data/electric_slide.gemspec
CHANGED
@@ -23,9 +23,9 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.rubygems_version = "1.2.0"
|
24
24
|
s.summary = "Automatic Call Distributor for Adhearsion"
|
25
25
|
|
26
|
-
s.add_runtime_dependency 'adhearsion'
|
26
|
+
s.add_runtime_dependency 'adhearsion', ['>= 2.5.0', '< 3.0.0']
|
27
27
|
s.add_runtime_dependency 'countdownlatch'
|
28
|
-
s.add_runtime_dependency 'activesupport'
|
28
|
+
s.add_runtime_dependency 'activesupport', ['<= 3.2.13']
|
29
29
|
s.add_development_dependency 'rspec', ['~> 3.0']
|
30
30
|
s.add_development_dependency 'timecop'
|
31
31
|
s.add_development_dependency 'ci_reporter'
|
@@ -34,5 +34,9 @@ Gem::Specification.new do |s|
|
|
34
34
|
s.add_development_dependency 'simplecov'
|
35
35
|
s.add_development_dependency 'simplecov-rcov'
|
36
36
|
|
37
|
+
# These two are needed to keep compatibility with Ruby >= 2.2.0, <= 2.2.3
|
38
|
+
s.add_development_dependency 'ruby_dep', ['= 1.3.1']
|
39
|
+
s.add_development_dependency 'listen', ['<= 3.1.1']
|
40
|
+
|
37
41
|
s.specification_version = 2
|
38
42
|
end
|
data/lib/electric_slide/agent.rb
CHANGED
@@ -15,6 +15,22 @@ class ElectricSlide
|
|
15
15
|
@presence = opts[:presence] || :available
|
16
16
|
end
|
17
17
|
|
18
|
+
# Updates an agent instance's attributes.
|
19
|
+
# @param [Hash] opts Agent attributes
|
20
|
+
# @return {Agent}
|
21
|
+
def update(attrs = {})
|
22
|
+
unless Hash === attrs
|
23
|
+
raise ArgumentError.new('Agent attributes must be a hash')
|
24
|
+
end
|
25
|
+
|
26
|
+
attrs.each do |attr, value|
|
27
|
+
setter = "#{attr}="
|
28
|
+
send setter, value
|
29
|
+
end
|
30
|
+
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
18
34
|
def update_presence(new_presence, extra_params = {})
|
19
35
|
old_presence = @presence
|
20
36
|
@presence = new_presence
|
@@ -68,5 +84,10 @@ class ElectricSlide
|
|
68
84
|
def from
|
69
85
|
@call.from
|
70
86
|
end
|
87
|
+
|
88
|
+
# Returns `true` if the agent can be called, i.e., has a contact address
|
89
|
+
def callable?
|
90
|
+
address.present?
|
91
|
+
end
|
71
92
|
end
|
72
93
|
end
|
@@ -165,13 +165,7 @@ class ElectricSlide
|
|
165
165
|
abort DuplicateAgentError.new("Agent is already in the queue") if get_agent(agent.id)
|
166
166
|
|
167
167
|
agent.queue = current_actor
|
168
|
-
|
169
|
-
case @connection_type
|
170
|
-
when :call
|
171
|
-
abort ArgumentError.new("Agent has no callable address") unless agent.address
|
172
|
-
when :bridge
|
173
|
-
bridged_agent_health_check agent
|
174
|
-
end
|
168
|
+
accept_agent! agent
|
175
169
|
|
176
170
|
logger.info "Adding agent #{agent} to the queue"
|
177
171
|
@agents << agent
|
@@ -182,6 +176,24 @@ class ElectricSlide
|
|
182
176
|
async.check_for_connections
|
183
177
|
end
|
184
178
|
|
179
|
+
# Updates a queued agent's attributes
|
180
|
+
def update_agent(agent, agent_attrs)
|
181
|
+
abort ArgumentError.new('Agent must not be `nil`') unless agent
|
182
|
+
unless get_agent(agent.id)
|
183
|
+
abort MissingAgentError.new('Agent is not in the queue')
|
184
|
+
end
|
185
|
+
|
186
|
+
# check if the agent is allowed to have the given set of attributes using
|
187
|
+
# a dupe, to preserve the state of the original in case of failure
|
188
|
+
agent.dup.tap do |double_agent|
|
189
|
+
double_agent.update agent_attrs
|
190
|
+
accept_agent! double_agent
|
191
|
+
end
|
192
|
+
|
193
|
+
agent.update agent_attrs
|
194
|
+
return_agent agent, agent.presence
|
195
|
+
end
|
196
|
+
|
185
197
|
# Marks an agent as available to take a call. To be called after an agent completes a call
|
186
198
|
# and is ready to take the next call.
|
187
199
|
# @param [Agent] agent The {Agent} that is being returned to the queue
|
@@ -276,9 +288,10 @@ class ElectricSlide
|
|
276
288
|
# @param [Agent] agent Agent to be connected
|
277
289
|
# @param [Adhearsion::Call] call Caller to be connected
|
278
290
|
def connect(agent, queued_call)
|
279
|
-
unless queued_call.active?
|
291
|
+
unless queued_call && queued_call.active?
|
280
292
|
logger.warn "Inactive queued call found in #connect"
|
281
293
|
return_agent agent
|
294
|
+
return
|
282
295
|
end
|
283
296
|
|
284
297
|
queued_call[:agent] = agent
|
@@ -288,15 +301,11 @@ class ElectricSlide
|
|
288
301
|
when :call
|
289
302
|
call_agent agent, queued_call
|
290
303
|
when :bridge
|
291
|
-
unless agent.call && agent.call.active?
|
292
|
-
logger.warn "Inactive agent call found in #connect, returning caller to queue"
|
293
|
-
priority_enqueue queued_call
|
294
|
-
end
|
295
304
|
bridge_agent agent, queued_call
|
296
305
|
end
|
297
306
|
rescue *ENDED_CALL_EXCEPTIONS
|
298
307
|
ignoring_ended_calls do
|
299
|
-
if queued_call.active?
|
308
|
+
if queued_call && queued_call.active?
|
300
309
|
logger.warn "Dead call exception in #connect but queued_call still alive, reinserting into queue"
|
301
310
|
priority_enqueue queued_call
|
302
311
|
end
|
@@ -392,9 +401,9 @@ class ElectricSlide
|
|
392
401
|
|
393
402
|
agent_call.on_end do |end_event|
|
394
403
|
# Ensure we don't return an agent that was removed or paused
|
404
|
+
old_call = agent.call
|
395
405
|
conditionally_return_agent agent
|
396
|
-
|
397
|
-
agent.call = nil
|
406
|
+
agent.call = nil if agent_return_method == :manual || agent.call == old_call
|
398
407
|
|
399
408
|
agent.callback :disconnect, queue, agent_call, queued_call
|
400
409
|
|
@@ -418,6 +427,13 @@ class ElectricSlide
|
|
418
427
|
end
|
419
428
|
|
420
429
|
def bridge_agent(agent, queued_call)
|
430
|
+
unless agent.call && agent.call.active?
|
431
|
+
logger.warn "Inactive agent call found for Agent #{agent.id} while bridging. Logging out agent and returning caller to queue."
|
432
|
+
priority_enqueue queued_call
|
433
|
+
remove_agent agent
|
434
|
+
return
|
435
|
+
end
|
436
|
+
|
421
437
|
# Stash caller ID to make log messages work even if calls end
|
422
438
|
queued_caller_id = remote_party queued_call
|
423
439
|
agent.call[:queued_call] = queued_call
|
@@ -455,6 +471,17 @@ class ElectricSlide
|
|
455
471
|
end
|
456
472
|
end
|
457
473
|
|
474
|
+
def accept_agent!(agent)
|
475
|
+
case @connection_type
|
476
|
+
when :call
|
477
|
+
unless agent.callable?
|
478
|
+
abort ArgumentError.new('Agent has no callable address')
|
479
|
+
end
|
480
|
+
when :bridge
|
481
|
+
bridged_agent_health_check agent
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
458
485
|
# @private
|
459
486
|
def bridged_agent_health_check(agent)
|
460
487
|
if agent.presence == :available && @connection_type == :bridge
|
@@ -63,4 +63,56 @@ describe ElectricSlide::Agent do
|
|
63
63
|
expect(old_presence).to eq :unavailable
|
64
64
|
expect(extra_params[:triggered_by]).to eq 'auto'
|
65
65
|
end
|
66
|
+
|
67
|
+
describe '#update' do
|
68
|
+
it 'returns the agent' do
|
69
|
+
expect(subject.update).to eq(subject)
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when given a hash with an agent attribute as key' do
|
73
|
+
it "sets the corresponding agent's attribute to the given value" do
|
74
|
+
expect {
|
75
|
+
subject.update(address: '456@bar.net')
|
76
|
+
}.to change(subject, :address).from('123@foo.com').to('456@bar.net')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when given an non-hash argument' do
|
81
|
+
it 'raises an error' do
|
82
|
+
expect {
|
83
|
+
subject.update(nil)
|
84
|
+
}.to raise_error(ArgumentError, 'Agent attributes must be a hash')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when given a hash with a key that does not correspond to any agent attribute' do
|
89
|
+
it "raises an error" do
|
90
|
+
expect {
|
91
|
+
subject.update(blah: 1)
|
92
|
+
}.to raise_error(NoMethodError)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#callable?' do
|
98
|
+
context 'when the agent has an address' do
|
99
|
+
before do
|
100
|
+
subject.address = 'Baker St.'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'returns `true`' do
|
104
|
+
expect(subject).to be_callable
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when the agent has no address' do
|
109
|
+
before do
|
110
|
+
subject.address = ''
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns `false`' do
|
114
|
+
expect(subject).to_not be_callable
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
66
118
|
end
|
@@ -54,6 +54,7 @@ describe ElectricSlide::CallQueue do
|
|
54
54
|
let(:agent_id) { '123' }
|
55
55
|
let(:agent) { ElectricSlide::Agent.new id: agent_id, address: '123', presence: :available }
|
56
56
|
let!(:agent_call) { Adhearsion::OutboundCall.new }
|
57
|
+
let!(:new_agent_call) { Adhearsion::OutboundCall.new }
|
57
58
|
let(:queued_call) { dummy_call }
|
58
59
|
let(:connected_time) { DateTime.now }
|
59
60
|
|
@@ -71,6 +72,7 @@ describe ElectricSlide::CallQueue do
|
|
71
72
|
|
72
73
|
before do
|
73
74
|
allow(agent_call).to receive(:dial)
|
75
|
+
allow(new_agent_call).to receive(:dial)
|
74
76
|
queue.add_agent agent
|
75
77
|
queue.enqueue queued_call
|
76
78
|
end
|
@@ -101,6 +103,16 @@ describe ElectricSlide::CallQueue do
|
|
101
103
|
}.to change(agent, :call).from(agent_call).to(nil)
|
102
104
|
end
|
103
105
|
|
106
|
+
it "does not unsets the agent's `call` attribute when the agent connects to another call" do
|
107
|
+
queue.remove_agent double_agent # We only want one agent in the queue so they get the next call
|
108
|
+
allow(Adhearsion::OutboundCall).to receive(:new) { new_agent_call }
|
109
|
+
queue.enqueue dummy_call # We want another call to be waiting after this call
|
110
|
+
|
111
|
+
expect {
|
112
|
+
agent_call << Punchblock::Event::End.new(reason: :hangup)
|
113
|
+
}.to change(agent, :call).from(agent_call).to(new_agent_call)
|
114
|
+
end
|
115
|
+
|
104
116
|
context "when the return strategy is :auto" do
|
105
117
|
let(:agent_return_method) { :auto }
|
106
118
|
|
@@ -287,6 +299,54 @@ describe ElectricSlide::CallQueue do
|
|
287
299
|
end
|
288
300
|
end
|
289
301
|
|
302
|
+
describe '#update_agent' do
|
303
|
+
let(:queue) { ElectricSlide::CallQueue.new(connection_type: :call) }
|
304
|
+
let(:agent) { ElectricSlide::Agent.new(id: '1', address: 'agent@example.com', presence: :on_call) }
|
305
|
+
|
306
|
+
before do
|
307
|
+
queue.add_agent(agent)
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'updates the agent with the given attributes' do
|
311
|
+
expect {
|
312
|
+
queue.update_agent(agent, address: 'reagent@acme.com')
|
313
|
+
}.to change(agent, :address).from('agent@example.com').to('reagent@acme.com')
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'returns the agent to the queue' do
|
317
|
+
expect(queue.wrapped_object).to receive(:return_agent).with(agent, :on_call)
|
318
|
+
queue.update_agent(agent, address: 'reagent@acme.com')
|
319
|
+
end
|
320
|
+
|
321
|
+
context 'when given a set of attributes that makes the agent unacceptable in the queue' do
|
322
|
+
it 'raises an error' do
|
323
|
+
expect {
|
324
|
+
queue.update_agent(agent, address: '')
|
325
|
+
}.to raise_error(ArgumentError, 'Agent has no callable address')
|
326
|
+
end
|
327
|
+
|
328
|
+
it "does not change the agent's attributes" do
|
329
|
+
expect { queue.update_agent(agent, address: '') }.to raise_error(ArgumentError)
|
330
|
+
|
331
|
+
expect(agent.id).to eq('1')
|
332
|
+
expect(agent.address).to eq('agent@example.com')
|
333
|
+
expect(agent.presence).to eq(:on_call)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'when given an agent not in the queue' do
|
338
|
+
before do
|
339
|
+
queue.remove_agent agent
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'raises an error' do
|
343
|
+
expect {
|
344
|
+
queue.update_agent(agent, address: 'ghost@imf.com')
|
345
|
+
}.to raise_error(ElectricSlide::CallQueue::MissingAgentError, 'Agent is not in the queue')
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
290
350
|
describe '#return_agent' do
|
291
351
|
let(:queue) { ElectricSlide::CallQueue.new }
|
292
352
|
let(:agent) { ElectricSlide::Agent.new(id: '1', address: 'agent@example.com', presence: :on_call) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: electric_slide
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Klang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: adhearsion
|
@@ -16,14 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.5.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
29
|
+
version: 2.5.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: countdownlatch
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,16 +48,16 @@ dependencies:
|
|
42
48
|
name: activesupport
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
44
50
|
requirements:
|
45
|
-
- - "
|
51
|
+
- - "<="
|
46
52
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
53
|
+
version: 3.2.13
|
48
54
|
type: :runtime
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
51
57
|
requirements:
|
52
|
-
- - "
|
58
|
+
- - "<="
|
53
59
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
60
|
+
version: 3.2.13
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
62
|
name: rspec
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +156,34 @@ dependencies:
|
|
150
156
|
- - ">="
|
151
157
|
- !ruby/object:Gem::Version
|
152
158
|
version: '0'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: ruby_dep
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - '='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: 1.3.1
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - '='
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: 1.3.1
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: listen
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - "<="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: 3.1.1
|
180
|
+
type: :development
|
181
|
+
prerelease: false
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - "<="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: 3.1.1
|
153
187
|
description: Automatic Call Distributor (ACD) for Adhearsion. Currently implements
|
154
188
|
only Round Robin distribution strategies.
|
155
189
|
email: dev&adhearsion.com
|
@@ -157,10 +191,10 @@ executables: []
|
|
157
191
|
extensions: []
|
158
192
|
extra_rdoc_files: []
|
159
193
|
files:
|
194
|
+
- ".github/workflows/build.yml"
|
160
195
|
- ".gitignore"
|
161
196
|
- ".lgtm"
|
162
197
|
- ".rspec"
|
163
|
-
- ".travis.yml"
|
164
198
|
- CHANGELOG.md
|
165
199
|
- Gemfile
|
166
200
|
- Guardfile
|
@@ -201,8 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
235
|
- !ruby/object:Gem::Version
|
202
236
|
version: '0'
|
203
237
|
requirements: []
|
204
|
-
|
205
|
-
rubygems_version: 2.5.1
|
238
|
+
rubygems_version: 3.1.6
|
206
239
|
signing_key:
|
207
240
|
specification_version: 2
|
208
241
|
summary: Automatic Call Distributor for Adhearsion
|