electric_slide 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|